Finish Project

This commit is contained in:
2026-02-04 11:00:00 +01:00
parent 544fd29a8c
commit 4063d7ac3c
60 changed files with 1573 additions and 103 deletions

6
.idea/compiler.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="TypeScriptCompiler">
<option name="useServicePoweredTypesWasEnabledByExperiment" value="true" />
</component>
</project>

12
.idea/dataSources.xml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="ticket_sys@localhost" uuid="3d7ee8f1-4863-4cbc-8c64-6945c4151623">
<driver-ref>mysql.8</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>com.mysql.cj.jdbc.Driver</jdbc-driver>
<jdbc-url>jdbc:mysql://localhost:3306/ticket_sys</jdbc-url>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

6
.idea/data_source_mapping.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourcePerFileMappings">
<file url="file://$APPLICATION_CONFIG_DIR$/consoles/db/3d7ee8f1-4863-4cbc-8c64-6945c4151623/console.sql" value="3d7ee8f1-4863-4cbc-8c64-6945c4151623" />
</component>
</project>

View File

@@ -0,0 +1,5 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Next.js: debug client-side" type="JavascriptDebugType" uri="http://localhost:3000/" useFirstLineBreakpoints="true">
<method v="2" />
</configuration>
</component>

6
.idea/sqldialects.xml generated Normal file
View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="SqlDialectMappings">
<file url="PROJECT" dialect="MySQL" />
</component>
</project>

509
package-lock.json generated
View File

@@ -8,6 +8,7 @@
"name": "frontend-next", "name": "frontend-next",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"@storefront-ui/react": "^4.0.0",
"bcrypt": "^6.0.0", "bcrypt": "^6.0.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"next": "^16.1.4", "next": "^16.1.4",
@@ -42,9 +43,9 @@
} }
}, },
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.28.6", "version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz",
"integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -98,14 +99,14 @@
} }
}, },
"node_modules/@babel/generator": { "node_modules/@babel/generator": {
"version": "7.28.6", "version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.0.tgz",
"integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "integrity": "sha512-vSH118/wwM/pLR38g/Sgk05sNtro6TlTJKuiMXDaZqPUfjTFcudpCOt00IhOfj+1BFAX+UFAlzCU+6WXr3GLFQ==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/parser": "^7.28.6", "@babel/parser": "^7.29.0",
"@babel/types": "^7.28.6", "@babel/types": "^7.29.0",
"@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/gen-mapping": "^0.3.12",
"@jridgewell/trace-mapping": "^0.3.28", "@jridgewell/trace-mapping": "^0.3.28",
"jsesc": "^3.0.2" "jsesc": "^3.0.2"
@@ -218,13 +219,13 @@
} }
}, },
"node_modules/@babel/parser": { "node_modules/@babel/parser": {
"version": "7.28.6", "version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz",
"integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==",
"dev": true, "dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@babel/types": "^7.28.6" "@babel/types": "^7.29.0"
}, },
"bin": { "bin": {
"parser": "bin/babel-parser.js" "parser": "bin/babel-parser.js"
@@ -233,6 +234,15 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@babel/runtime": {
"version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz",
"integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
}
},
"node_modules/@babel/template": { "node_modules/@babel/template": {
"version": "7.28.6", "version": "7.28.6",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz",
@@ -268,9 +278,9 @@
} }
}, },
"node_modules/@babel/types": { "node_modules/@babel/types": {
"version": "7.28.6", "version": "7.29.0",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz",
"integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==",
"devOptional": true, "devOptional": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
@@ -458,6 +468,31 @@
"node": "^18.18.0 || ^20.9.0 || >=21.1.0" "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
} }
}, },
"node_modules/@floating-ui/core": {
"version": "1.7.4",
"resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz",
"integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==",
"license": "MIT",
"dependencies": {
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/dom": {
"version": "1.7.5",
"resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz",
"integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==",
"license": "MIT",
"dependencies": {
"@floating-ui/core": "^1.7.4",
"@floating-ui/utils": "^0.2.10"
}
},
"node_modules/@floating-ui/utils": {
"version": "0.2.10",
"resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz",
"integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==",
"license": "MIT"
},
"node_modules/@humanfs/core": { "node_modules/@humanfs/core": {
"version": "0.19.1", "version": "0.19.1",
"resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -1012,7 +1047,6 @@
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
"integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@jridgewell/trace-mapping": { "node_modules/@jridgewell/trace-mapping": {
@@ -1238,6 +1272,131 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@storefront-ui/react": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@storefront-ui/react/-/react-4.0.0.tgz",
"integrity": "sha512-yj3dKvxGJH4IkT+oh0+Q1WIcOBVhFuYMHFz6eLjJo/+hMRJVROkSe/lmaKo35iXQ4p8/DuIIkC2ZhM4VCLbF1w==",
"license": "MIT",
"dependencies": {
"@floating-ui/react-dom": "^2.0.9",
"@storefront-ui/shared": "3.1.0",
"@storefront-ui/tailwind-config": "3.1.0",
"classnames": "^2.5.1",
"jw-paginate": "^1.0.4",
"react-polymorphed": "^2.1.0",
"react-transition-group": "^4.4.5",
"react-use": "^17.5.1",
"tabbable": "^6.1.1",
"tailwind-merge": "^3.3.1"
},
"peerDependencies": {
"react": "^19.0.0"
}
},
"node_modules/@storefront-ui/react/node_modules/@floating-ui/react-dom": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz",
"integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==",
"license": "MIT",
"dependencies": {
"@floating-ui/dom": "^1.7.5"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/@storefront-ui/react/node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"license": "BSD-3-Clause",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/@storefront-ui/react/node_modules/react-use": {
"version": "17.6.0",
"resolved": "https://registry.npmjs.org/react-use/-/react-use-17.6.0.tgz",
"integrity": "sha512-OmedEScUMKFfzn1Ir8dBxiLLSOzhKe/dPZwVxcujweSj45aNM7BEGPb9BEVIgVEqEXx6f3/TsXzwIktNgUR02g==",
"license": "Unlicense",
"dependencies": {
"@types/js-cookie": "^2.2.6",
"@xobotyi/scrollbar-width": "^1.9.5",
"copy-to-clipboard": "^3.3.1",
"fast-deep-equal": "^3.1.3",
"fast-shallow-equal": "^1.0.0",
"js-cookie": "^2.2.1",
"nano-css": "^5.6.2",
"react-universal-interface": "^0.6.2",
"resize-observer-polyfill": "^1.5.1",
"screenfull": "^5.1.0",
"set-harmonic-interval": "^1.0.1",
"throttle-debounce": "^3.0.1",
"ts-easing": "^0.2.0",
"tslib": "^2.1.0"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
}
},
"node_modules/@storefront-ui/react/node_modules/react-use/node_modules/nano-css": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/nano-css/-/nano-css-5.6.2.tgz",
"integrity": "sha512-+6bHaC8dSDGALM1HJjOHVXpuastdu2xFoZlC77Jh4cg+33Zcgm+Gxd+1xsnpZK14eyHObSp82+ll5y3SX75liw==",
"license": "Unlicense",
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.15",
"css-tree": "^1.1.2",
"csstype": "^3.1.2",
"fastest-stable-stringify": "^2.0.2",
"inline-style-prefixer": "^7.0.1",
"rtl-css-js": "^1.16.1",
"stacktrace-js": "^2.0.2",
"stylis": "^4.3.0"
},
"peerDependencies": {
"react": "*",
"react-dom": "*"
}
},
"node_modules/@storefront-ui/shared": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@storefront-ui/shared/-/shared-3.1.0.tgz",
"integrity": "sha512-TqYPguDt8EBit1ukAM35jxVnwNvmIYrsOddYk0qN3Ij9MNEgXNGYYXczJCU3Ct2UHbzY9VTfR/JKA06uBYJrmw==",
"license": "MIT"
},
"node_modules/@storefront-ui/tailwind-config": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@storefront-ui/tailwind-config/-/tailwind-config-3.1.0.tgz",
"integrity": "sha512-f2qeb7ii2gWfjlkBLhQ7H2jpntu3G7aoxixlTcIfL0b38cNZPPxCsk5y40bptQ42gkJh10/Ne0eoM5YpLDNM8Q==",
"license": "MIT",
"dependencies": {
"@storefront-ui/tw-plugin-peer-next": "3.1.0",
"@tailwindcss/container-queries": "^0.1.1"
},
"peerDependencies": {
"@tailwindcss/typography": ">=0.5.19",
"tailwindcss": ">= 4.0.0-alpha.20 || >= 4.0.0-beta.1 || >= 4.0.0"
}
},
"node_modules/@storefront-ui/tw-plugin-peer-next": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@storefront-ui/tw-plugin-peer-next/-/tw-plugin-peer-next-3.1.0.tgz",
"integrity": "sha512-690GvYL97S9YSI6dqtNKfEo/n6dJJXT5iq196yJWT0FmDi9I3uGdcN5+F419V4jRmePsYDhptlZCaZ6HQjwlNQ==",
"license": "MIT",
"peerDependencies": {
"tailwindcss": ">= 4.0.0-alpha.20 || >= 4.0.0-beta.1 || >= 4.0.0"
}
},
"node_modules/@swc/helpers": { "node_modules/@swc/helpers": {
"version": "0.5.15", "version": "0.5.15",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
@@ -1247,6 +1406,15 @@
"tslib": "^2.8.0" "tslib": "^2.8.0"
} }
}, },
"node_modules/@tailwindcss/container-queries": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/@tailwindcss/container-queries/-/container-queries-0.1.1.tgz",
"integrity": "sha512-p18dswChx6WnTSaJCSGx6lTmrGzNNvm2FtXmiO6AuA1V4U5REyoqwmT6kgAsIMdjo07QdAfYXHJ4hnMtfHzWgA==",
"license": "MIT",
"peerDependencies": {
"tailwindcss": ">=3.2.0"
}
},
"node_modules/@tailwindcss/node": { "node_modules/@tailwindcss/node": {
"version": "4.1.18", "version": "4.1.18",
"resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
@@ -1518,6 +1686,19 @@
"tailwindcss": "4.1.18" "tailwindcss": "4.1.18"
} }
}, },
"node_modules/@tailwindcss/typography": {
"version": "0.5.19",
"resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz",
"integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==",
"license": "MIT",
"peer": true,
"dependencies": {
"postcss-selector-parser": "6.0.10"
},
"peerDependencies": {
"tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1"
}
},
"node_modules/@tybys/wasm-util": { "node_modules/@tybys/wasm-util": {
"version": "0.10.1", "version": "0.10.1",
"resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz",
@@ -1546,6 +1727,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/js-cookie": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@types/js-cookie/-/js-cookie-2.2.7.tgz",
"integrity": "sha512-aLkWa0C0vO5b4Sr798E26QgOkss68Un0bLjs7u9qxzPT5CG+8DuNTffWES58YzJs3hrVAOs1wonycqEBqNJubA==",
"license": "MIT"
},
"node_modules/@types/json-schema": { "node_modules/@types/json-schema": {
"version": "7.0.15", "version": "7.0.15",
"resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1574,7 +1761,6 @@
"version": "19.2.10", "version": "19.2.10",
"resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.10.tgz",
"integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==", "integrity": "sha512-WPigyYuGhgZ/cTPRXB2EwUw+XvsRA3GqHlsP4qteqrnnjDrApbS7MxcGr/hke5iUoeB7E/gQtrs9I37zAJ0Vjw==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"csstype": "^3.2.2" "csstype": "^3.2.2"
@@ -2128,6 +2314,12 @@
"win32" "win32"
] ]
}, },
"node_modules/@xobotyi/scrollbar-width": {
"version": "1.9.5",
"resolved": "https://registry.npmjs.org/@xobotyi/scrollbar-width/-/scrollbar-width-1.9.5.tgz",
"integrity": "sha512-N8tkAACJx2ww8vFMneJmaAgmjAG1tnVBZJRLRcx061tmsLRZHSEZSLuGWnwPtunsSLvSqXQ2wfp7Mgqg1I+2dQ==",
"license": "MIT"
},
"node_modules/acorn": { "node_modules/acorn": {
"version": "8.15.0", "version": "8.15.0",
"resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -2609,6 +2801,12 @@
"url": "https://github.com/chalk/chalk?sponsor=1" "url": "https://github.com/chalk/chalk?sponsor=1"
} }
}, },
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==",
"license": "MIT"
},
"node_modules/client-only": { "node_modules/client-only": {
"version": "0.0.1", "version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@@ -2658,6 +2856,15 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/copy-to-clipboard": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz",
"integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==",
"license": "MIT",
"dependencies": {
"toggle-selection": "^1.0.6"
}
},
"node_modules/cross-spawn": { "node_modules/cross-spawn": {
"version": "7.0.6", "version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
@@ -2673,11 +2880,45 @@
"node": ">= 8" "node": ">= 8"
} }
}, },
"node_modules/css-in-js-utils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/css-in-js-utils/-/css-in-js-utils-3.1.0.tgz",
"integrity": "sha512-fJAcud6B3rRu+KHYk+Bwf+WFL2MDCJJ1XG9x137tJQ0xYxor7XziQtuGFbWNdqrvF4Tk26O3H73nfVqXt/fW1A==",
"license": "MIT",
"dependencies": {
"hyphenate-style-name": "^1.0.3"
}
},
"node_modules/css-tree": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/css-tree/-/css-tree-1.1.3.tgz",
"integrity": "sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==",
"license": "MIT",
"dependencies": {
"mdn-data": "2.0.14",
"source-map": "^0.6.1"
},
"engines": {
"node": ">=8.0.0"
}
},
"node_modules/cssesc": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
"integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
"license": "MIT",
"peer": true,
"bin": {
"cssesc": "bin/cssesc"
},
"engines": {
"node": ">=4"
}
},
"node_modules/csstype": { "node_modules/csstype": {
"version": "3.2.3", "version": "3.2.3",
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
"integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/damerau-levenshtein": { "node_modules/damerau-levenshtein": {
@@ -2825,6 +3066,16 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dunder-proto": { "node_modules/dunder-proto": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
@@ -2868,6 +3119,15 @@
"node": ">=10.13.0" "node": ">=10.13.0"
} }
}, },
"node_modules/error-stack-parser": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/error-stack-parser/-/error-stack-parser-2.1.4.tgz",
"integrity": "sha512-Sk5V6wVazPhq5MhpO+AUxJn5x7XSXGl1R93Vn7i+zS15KDVxQijejNCrz8340/2bgLBjR9GtEG8ZVKONDjcqGQ==",
"license": "MIT",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/es-abstract": { "node_modules/es-abstract": {
"version": "1.24.1", "version": "1.24.1",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.1.tgz",
@@ -3496,7 +3756,6 @@
"version": "3.1.3", "version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-glob": { "node_modules/fast-glob": {
@@ -3543,6 +3802,17 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/fast-shallow-equal": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz",
"integrity": "sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw=="
},
"node_modules/fastest-stable-stringify": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz",
"integrity": "sha512-bijHueCGd0LqqNK9b5oCMHc0MluJAx0cwqASgbWMvkO01lCYgIhacVRLcaDz3QnyYIRNJRDwMb41VuT6pHJ91Q==",
"license": "MIT"
},
"node_modules/fastq": { "node_modules/fastq": {
"version": "1.20.1", "version": "1.20.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz",
@@ -3938,6 +4208,12 @@
"hermes-estree": "0.25.1" "hermes-estree": "0.25.1"
} }
}, },
"node_modules/hyphenate-style-name": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz",
"integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==",
"license": "BSD-3-Clause"
},
"node_modules/ignore": { "node_modules/ignore": {
"version": "5.3.2", "version": "5.3.2",
"resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3975,6 +4251,15 @@
"node": ">=0.8.19" "node": ">=0.8.19"
} }
}, },
"node_modules/inline-style-prefixer": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/inline-style-prefixer/-/inline-style-prefixer-7.0.1.tgz",
"integrity": "sha512-lhYo5qNTQp3EvSSp3sRvXMbVQTLrvGV6DycRMJ5dm2BLMiJ30wpXKdDdgX+GmJZ5uQMucwRKHamXSst3Sj/Giw==",
"license": "MIT",
"dependencies": {
"css-in-js-utils": "^3.1.0"
}
},
"node_modules/internal-slot": { "node_modules/internal-slot": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz",
@@ -4447,11 +4732,16 @@
"jiti": "lib/jiti-cli.mjs" "jiti": "lib/jiti-cli.mjs"
} }
}, },
"node_modules/js-cookie": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz",
"integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==",
"license": "MIT"
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
"integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/js-yaml": { "node_modules/js-yaml": {
@@ -4530,6 +4820,12 @@
"node": ">=4.0" "node": ">=4.0"
} }
}, },
"node_modules/jw-paginate": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/jw-paginate/-/jw-paginate-1.0.4.tgz",
"integrity": "sha512-W0bv782exgCoynUL/egbRpaYwf/r6T6e02H870H5u3hfSgEYrxgz5POwmFF5aApS6iPi6yhZ0VF8IbafNFsntA==",
"license": "MIT"
},
"node_modules/keyv": { "node_modules/keyv": {
"version": "4.5.4", "version": "4.5.4",
"resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
@@ -4862,7 +5158,6 @@
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
"integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"js-tokens": "^3.0.0 || ^4.0.0" "js-tokens": "^3.0.0 || ^4.0.0"
@@ -4901,6 +5196,12 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/mdn-data": {
"version": "2.0.14",
"resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.0.14.tgz",
"integrity": "sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==",
"license": "CC0-1.0"
},
"node_modules/merge2": { "node_modules/merge2": {
"version": "1.4.1", "version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
@@ -5108,7 +5409,6 @@
"version": "4.1.1", "version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
"dev": true,
"license": "MIT", "license": "MIT",
"engines": { "engines": {
"node": ">=0.10.0" "node": ">=0.10.0"
@@ -5393,6 +5693,20 @@
"node": "^10 || ^12 || >=14" "node": "^10 || ^12 || >=14"
} }
}, },
"node_modules/postcss-selector-parser": {
"version": "6.0.10",
"resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz",
"integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==",
"license": "MIT",
"peer": true,
"dependencies": {
"cssesc": "^3.0.0",
"util-deprecate": "^1.0.2"
},
"engines": {
"node": ">=4"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -5407,7 +5721,6 @@
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
@@ -5471,9 +5784,26 @@
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/react-polymorphed": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/react-polymorphed/-/react-polymorphed-2.2.2.tgz",
"integrity": "sha512-8pv0VvOHITcmGs+E/FNAuE+4IZpIaKEThCJsvmp+NC54Lq5YbjHtPX0Af6H6MazhEQ7WQyJBt6b6aY+7WUytJw==",
"license": "MIT",
"peerDependencies": {
"@types/react": ">=16.8"
}
},
"node_modules/react-universal-interface": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz",
"integrity": "sha512-dg8yXdcQmvgR13RIlZbTRQOoUrDciFVoSBZILwjE2LFISxZZ8loVJKAkuzswl5js8BHda79bIb2b84ehU8IjXw==",
"peerDependencies": {
"react": "*",
"tslib": "*"
}
},
"node_modules/reflect.getprototypeof": { "node_modules/reflect.getprototypeof": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz",
@@ -5518,6 +5848,12 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/resize-observer-polyfill": {
"version": "1.5.1",
"resolved": "https://registry.npmjs.org/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz",
"integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==",
"license": "MIT"
},
"node_modules/resolve": { "node_modules/resolve": {
"version": "1.22.11", "version": "1.22.11",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz",
@@ -5570,6 +5906,15 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/rtl-css-js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/rtl-css-js/-/rtl-css-js-1.16.1.tgz",
"integrity": "sha512-lRQgou1mu19e+Ya0LsTvKrVJ5TYUbqCVPAiImX3UfLTenarvPUl1QFdvu5Z3PYmHT9RCcwIfbjRQBntExyj3Zg==",
"license": "MIT",
"dependencies": {
"@babel/runtime": "^7.1.2"
}
},
"node_modules/run-parallel": { "node_modules/run-parallel": {
"version": "1.2.0", "version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
@@ -5655,6 +6000,18 @@
"integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/screenfull": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/screenfull/-/screenfull-5.2.0.tgz",
"integrity": "sha512-9BakfsO2aUQN2K9Fdbj87RJIEZ82Q9IGim7FqM5OsebfoFC6ZHXgDq/KvniuLTPdeM8wY2o6Dj3WQ7KeQCj3cA==",
"license": "MIT",
"engines": {
"node": ">=0.10.0"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/semver": { "node_modules/semver": {
"version": "6.3.1", "version": "6.3.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
@@ -5699,6 +6056,15 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/set-harmonic-interval": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/set-harmonic-interval/-/set-harmonic-interval-1.0.1.tgz",
"integrity": "sha512-AhICkFV84tBP1aWqPwLZqFvAwqEoVA9kxNMniGEUvzOlm4vLmOFLiTT3UZ6bziJTy4bOVpzWGTfSCbmaayGx8g==",
"license": "Unlicense",
"engines": {
"node": ">=6.9"
}
},
"node_modules/set-proto": { "node_modules/set-proto": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz",
@@ -5871,6 +6237,15 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/source-map-js": { "node_modules/source-map-js": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
@@ -5887,6 +6262,51 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/stack-generator": {
"version": "2.0.10",
"resolved": "https://registry.npmjs.org/stack-generator/-/stack-generator-2.0.10.tgz",
"integrity": "sha512-mwnua/hkqM6pF4k8SnmZ2zfETsRUpWXREfA/goT8SLCV4iOFa4bzOX2nDipWAZFPTjLvQB82f5yaodMVhK0yJQ==",
"license": "MIT",
"dependencies": {
"stackframe": "^1.3.4"
}
},
"node_modules/stackframe": {
"version": "1.3.4",
"resolved": "https://registry.npmjs.org/stackframe/-/stackframe-1.3.4.tgz",
"integrity": "sha512-oeVtt7eWQS+Na6F//S4kJ2K2VbRlS9D43mAlMyVpVWovy9o+jfgH8O9agzANzaiLjclA0oYzUXEM4PurhSUChw==",
"license": "MIT"
},
"node_modules/stacktrace-gps": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/stacktrace-gps/-/stacktrace-gps-3.1.2.tgz",
"integrity": "sha512-GcUgbO4Jsqqg6RxfyTHFiPxdPqF+3LFmQhm7MgCuYQOYuWyqxo5pwRPz5d/u6/WYJdEnWfK4r+jGbyD8TSggXQ==",
"license": "MIT",
"dependencies": {
"source-map": "0.5.6",
"stackframe": "^1.3.4"
}
},
"node_modules/stacktrace-gps/node_modules/source-map": {
"version": "0.5.6",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.6.tgz",
"integrity": "sha512-MjZkVp0NHr5+TPihLcadqnlVoGIoWo4IBHptutGh9wI3ttUYvCG26HkSuDi+K6lsZ25syXJXcctwgyVCt//xqA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/stacktrace-js": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/stacktrace-js/-/stacktrace-js-2.0.2.tgz",
"integrity": "sha512-Je5vBeY4S1r/RnLydLl0TBTi3F2qdfWmYsGvtfZgEI+SCprPppaIhQf5nGcal4gI4cGpCV/duLcAzT1np6sQqg==",
"license": "MIT",
"dependencies": {
"error-stack-parser": "^2.0.6",
"stack-generator": "^2.0.5",
"stacktrace-gps": "^3.0.4"
}
},
"node_modules/stop-iteration-iterator": { "node_modules/stop-iteration-iterator": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz",
@@ -6060,6 +6480,12 @@
} }
} }
}, },
"node_modules/stylis": {
"version": "4.3.6",
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.3.6.tgz",
"integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==",
"license": "MIT"
},
"node_modules/supports-color": { "node_modules/supports-color": {
"version": "7.2.0", "version": "7.2.0",
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -6086,6 +6512,12 @@
"url": "https://github.com/sponsors/ljharb" "url": "https://github.com/sponsors/ljharb"
} }
}, },
"node_modules/tabbable": {
"version": "6.4.0",
"resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.4.0.tgz",
"integrity": "sha512-05PUHKSNE8ou2dwIxTngl4EzcnsCDZGJ/iCLtDflR/SHB/ny14rXc+qU5P4mG9JkusiV7EivzY9Mhm55AzAvCg==",
"license": "MIT"
},
"node_modules/tailwind-merge": { "node_modules/tailwind-merge": {
"version": "3.4.0", "version": "3.4.0",
"resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
@@ -6100,7 +6532,6 @@
"version": "4.1.18", "version": "4.1.18",
"resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
"integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
"dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/tapable": { "node_modules/tapable": {
@@ -6117,6 +6548,15 @@
"url": "https://opencollective.com/webpack" "url": "https://opencollective.com/webpack"
} }
}, },
"node_modules/throttle-debounce": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz",
"integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==",
"license": "MIT",
"engines": {
"node": ">=10"
}
},
"node_modules/tinyglobby": { "node_modules/tinyglobby": {
"version": "0.2.15", "version": "0.2.15",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
@@ -6178,6 +6618,12 @@
"node": ">=8.0" "node": ">=8.0"
} }
}, },
"node_modules/toggle-selection": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/toggle-selection/-/toggle-selection-1.0.6.tgz",
"integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==",
"license": "MIT"
},
"node_modules/ts-api-utils": { "node_modules/ts-api-utils": {
"version": "2.4.0", "version": "2.4.0",
"resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
@@ -6191,6 +6637,12 @@
"typescript": ">=4.8.4" "typescript": ">=4.8.4"
} }
}, },
"node_modules/ts-easing": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/ts-easing/-/ts-easing-0.2.0.tgz",
"integrity": "sha512-Z86EW+fFFh/IFB1fqQ3/+7Zpf9t2ebOAxNI/V6Wo7r5gqiqtxmgTlQ1qbqQcjLKYeSHPTsEmvlJUDg/EuL0uHQ==",
"license": "Unlicense"
},
"node_modules/tsconfig-paths": { "node_modules/tsconfig-paths": {
"version": "3.15.0", "version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
@@ -6454,6 +6906,13 @@
"punycode": "^2.1.0" "punycode": "^2.1.0"
} }
}, },
"node_modules/util-deprecate": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
"integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
"license": "MIT",
"peer": true
},
"node_modules/which": { "node_modules/which": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",

View File

@@ -9,6 +9,7 @@
"lint": "eslint" "lint": "eslint"
}, },
"dependencies": { "dependencies": {
"@storefront-ui/react": "^4.0.0",
"bcrypt": "^6.0.0", "bcrypt": "^6.0.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"next": "^16.1.4", "next": "^16.1.4",

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

@@ -1,16 +1,29 @@
"use client" "use client"
import {Button} from "@/components/Button/Button"; import {SingleCard} from "@/components/SingleCard/SingleCard";
import {Link} from "@/components/Link";
export default function Page() { export default function Page() {
return ( return (
<div className={"flex flex-col items-center justify-center h-full border-[#e20074]!"}> <div className={"flex flex-col items-center justify-between h-full "}>
<Button onClick={() => { <div className={"h-20"}/>
alert("WOW")
}} className={""}> <SingleCard className={"w-400 rounded-2xl! flex-col!"}>
Hallo Welt Knopf <a className={"md:text-4xl pb-4 pt-2"}>Gibt es ein Problem?</a>
</Button> <div className={"h-10"}/>
<Button typeStyle={"secondary"}>Tes23</Button> <Link href={"/new/ticket"} className={"mb-8"} onClick={() => {
<Button typeStyle={"tertiary"}>Tes23</Button> }} typeStyle={"primary"}>Problem melden</Link>
</SingleCard>
<div className={"h-10"}/>
<SingleCard className={"w-400 rounded-2xl! flex-col! bg-primary/15!"}>
<a className={"md:text-4xl pb-4 pt-2"}>Du möchtest deine Tickets ansehen?</a>
<div className={"h-10"}/>
<Link href={"/tickets"} className={"mb-8"} onClick={() => {
}} typeStyle={"primary"}>Zur Ticketansicht</Link>
</SingleCard>
<div className={"h-20"}/>
</div> </div>
) )

View File

@@ -1,8 +1,9 @@
import type { Metadata } from "next"; import type {Metadata} from "next";
import { Geist, Geist_Mono } from "next/font/google"; import {Geist, Geist_Mono} from "next/font/google";
import "@/app/globals.css"; import "@/app/globals.css";
import React from "react"; import React from "react";
import {Header} from "@/components/Header/Header"; import {Header} from "@/components/Header/Header";
import {Footer} from "@/components/Footer";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
@@ -28,11 +29,15 @@ export default function RootLayout({
return ( return (
<html lang="de" suppressHydrationWarning> <html lang="de" suppressHydrationWarning>
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased` } className={`${geistSans.variable} ${geistMono.variable} antialiased flex flex-col min-h-screen`}
suppressHydrationWarning suppressHydrationWarning
> >
<Header/> <Header/>
{children} <main className={"grow"}>
{children}
</main>
<Footer/>
</body> </body>
</html> </html>
); );

View File

@@ -0,0 +1,159 @@
'use client'
import { use, useEffect, useState, ChangeEvent } from 'react'
import { SingleCard } from "@/components/SingleCard/SingleCard";
import Form from "next/dist/client/form";
import { Button } from "@/components/Button";
import {getCategories, Ticket} from "@/components/Tickets";
import {sendRequestwTokenClient} from "@/app/actions/auth";
import {getUser} from "@/components/getUser";
import {useRouter} from "next/navigation";
export default function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const router = useRouter();
const StateMapping = ["Offen","Bearbeitet","Abgeschlossen"]
const PrioMapping = ["Niedrig","Mittel","Hoch","Notfall"]
// Initialer State leer, damit wir keine Fehler bei undefined haben
const [currentUser, setCurrentUser] = useState<string | null>()
const [currentTicket, setCurrentTicket] = useState<Ticket| null>({
category: undefined,
description: "",
priority: 1,
status: 1,
ticketID: 1,
ticketname: "",
username: ""
});
const [categories, setCategories] = useState<string[] | null>(null);
const { slug } = use(params);
const ticketId: number = Number(slug);
useEffect(() => {
const runAsync = async () => {
const category = await getCategories() || null;
const user = await getUser() || "none"
setCategories(category)
setCurrentUser(user)
setCurrentTicket((prev) => {return {...(prev as Ticket), username: user}})
}
runAsync();
}, [ticketId]);
// Zentraler Change-Handler für alle Inputs
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value } = e.target;
setCurrentTicket((prev) => {
if (!prev) return null;
return {
...prev,
[name]: value
};
});
};
const formAction = (e: FormData) => {
// Wenn du die Daten aus dem e: FormData Objekt ziehen willst (wie bisher):
const ticket: Ticket = {
status: Number(e.get("status")),
priority: Number(e.get("priority")),
category: e.get("category")?.toString(),
ticketname: e.get("ticketname")?.toString() || "none",
description: e.get("description")?.toString() || "none",
username: currentUser || "none",
};
sendRequestwTokenClient(`/ticket/create`, "POST",JSON.stringify(ticket))
router.push("/tickets")
};
if (!currentUser) {
return <a>Loading...</a>
}
return (
<div className={"flex flex-col"}>
<SingleCard>
<Form className={"flex flex-row grow justify-between"} action={formAction}>
<div className={"flex flex-col"}>
<div className={"flex flex-row"}>
<label className={"my-auto w-24"}>Ticketname</label>
<input
type={"text"}
name={"ticketname"}
className={"bg-secondary/25 m-2 p-1 w-80"}
value={currentTicket?.ticketname}
onChange={handleChange}
/>
</div>
<div className={"flex flex-row"}>
<label className={"my-auto w-24 min-w-24"}>Status</label>
<select
name={"status"}
className={"bg-secondary/25 rounded p-2 m-2 w-80"}
value={currentTicket?.status}
onChange={handleChange}
>
{
StateMapping?.map((value, index) => {
return (<option key={`${value}-${index}`} value={index +1}>{value}</option>)
})
}
</select>
</div>
<div className={"flex flex-row align-items-center"}>
<label className={"my-auto w-24 min-w-24"}>Kategorie</label>
<select
className={"bg-secondary/25 rounded p-2 m-2 w-80"}
name={"category"}
value={currentTicket?.category}
onChange={handleChange}
>
{categories && categories?.map((value, index) => {
return (<option key={`${value}-${index}`} value={value}>{value}</option>)
})}
</select>
</div>
<div className={"flex flex-row"}>
<label className={"my-auto w-24 min-w-24"}>Priorität</label>
<select
className={"bg-secondary/25 rounded p-2 m-2 w-80"}
name={"priority"}
value={currentTicket?.priority}
onChange={handleChange}
>
{
PrioMapping?.map((value, index) => {
return (<option key={`${value}-${index}`} value={index +1}>{value}</option>)
})
}
</select>
</div>
</div>
<div className={"flex flex-col px-20 grow"}>
<label className={"my-auto w-24 min-w-24 m-2"}>Beschreibung</label>
<textarea
name="description"
className="bg-secondary/25 m-2 grow p-2 min-h-10 resize-none overflow-hidden wrap-break-word focus:outline-none"
rows={3}
value={currentTicket?.description}
onChange={handleChange}
/>
</div>
<Button type={"submit"} className={"w-min p-4 h-min m-auto text-xl"}>Bearbeiten</Button>
</Form>
</SingleCard>
</div>
)
}

View File

@@ -0,0 +1,147 @@
'use client'
import { use, useEffect, useState, ChangeEvent } from 'react'
import { SingleCard } from "@/components/SingleCard/SingleCard";
import Form from "next/dist/client/form";
import { Button } from "@/components/Button";
import {DetailedTicket, getCategories, Ticket} from "@/components/Tickets";
import { getTicket } from "@/components/Tickets/getTicket";
import {sendRequestwTokenClient} from "@/app/actions/auth";
export default function Page({
params,
}: {
params: Promise<{ slug: string }>
}) {
const StateMapping = ["A","B","C"]
const PrioMapping = ["Niedrig","Mittel","Hoch","Notfall"]
// Initialer State leer, damit wir keine Fehler bei undefined haben
const [currentTicket, setCurrentTicket] = useState<DetailedTicket | null>(null);
const [categories, setCategories] = useState<string[] | null>(null);
const { slug } = use(params);
const ticketId: number = Number(slug);
useEffect(() => {
const runAsync = async () => {
const ticket = await getTicket(ticketId) || null;
const category = await getCategories() || null;
setCategories(category)
setCurrentTicket(ticket);
}
runAsync();
}, [ticketId]);
// Zentraler Change-Handler für alle Inputs
const handleChange = (e: ChangeEvent<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>) => {
const { name, value } = e.target;
console.log("IS CHANGED", name);
setCurrentTicket((prev) => {
if (!prev) return null;
return {
...prev,
[name]: value
};
});
};
const formAction = (e: FormData) => {
// Hier kannst du die Daten an den Server schicken
console.log("Form submitted with:", currentTicket);
// Wenn du die Daten aus dem e: FormData Objekt ziehen willst (wie bisher):
const ticket: Ticket = {
status: Number(e.get("status")),
priority: Number(e.get("priority")),
category: e.get("category")?.toString(),
ticketname: e.get("ticketname")?.toString() || "none",
description: e.get("description")?.toString() || "none",
username: currentTicket?.username || "none",
};
sendRequestwTokenClient(`/ticket/update/${ticketId}`, "POST",JSON.stringify(ticket))
alert("Gespeichert!");
};
if (!currentTicket) return <div>Lade Ticket...</div>;
console.log(currentTicket);
return (
<div className={"flex flex-col "}>
<SingleCard>
<Form className={"flex flex-row grow justify-between"} action={formAction}>
<div className={"flex flex-col"}>
<div className={"flex flex-row"}>
<label className={"my-auto w-24"}>Ticketname</label>
<input
type={"text"}
name={"ticketname"}
className={"bg-secondary/25 m-2 p-1 w-80"}
value={currentTicket.ticketname}
onChange={handleChange}
/>
</div>
<div className={"flex flex-row"}>
<label className={"my-auto w-24 min-w-24"}>Status</label>
<select
name={"status"}
className={"bg-secondary/25 rounded p-2 m-2 w-80"}
value={currentTicket.status}
onChange={handleChange}
>
{
StateMapping?.map((value, index) => {
return (<option key={`${value}-${index}`} value={index +1}>{value}</option>)
})
}
</select>
</div>
<div className={"flex flex-row align-items-center"}>
<label className={"my-auto w-24 min-w-24"}>Kategorie</label>
<select
className={"bg-secondary/25 rounded p-2 m-2 w-80"}
name={"category"}
value={currentTicket.category}
onChange={handleChange}
>
{categories && categories?.map((value, index) => {
return (<option key={`${value}-${index}`} value={value}>{value}</option>)
})}
</select>
</div>
<div className={"flex flex-row"}>
<label className={"my-auto w-24 min-w-24"}>Priorität</label>
<select
className={"bg-secondary/25 rounded p-2 m-2 w-80"}
name={"priority"}
value={currentTicket.priority}
onChange={handleChange}
>
{
PrioMapping?.map((value, index) => {
return (<option key={`${value}-${index}`} value={index +1}>{value}</option>)
})
}
</select>
</div>
</div>
<div className={"flex flex-col px-20 grow"}>
<label className={"my-auto w-24 min-w-24 m-2"}>Beschreibung</label>
<textarea
name="description"
className="bg-secondary/25 m-2 grow p-2 min-h-10 resize-none overflow-hidden wrap-break-word focus:outline-none"
rows={3}
value={currentTicket.description}
onChange={handleChange}
/>
</div>
<Button type={"submit"} className={"w-min p-4 h-min m-auto text-xl"}>Bearbeiten</Button>
</Form>
</SingleCard>
</div>
)
}

View File

@@ -0,0 +1,18 @@
'use server'
import React from "react";
import {SingleCard} from "@/components/SingleCard/SingleCard";
import {TicketTable} from "@/components/TicketTable";
export default async function Page() {
return (
<>
<SingleCard>
<a>Deine Tickets</a>
</SingleCard>
<TicketTable/>
<a></a>
</>
);
}

View File

@@ -2,6 +2,8 @@ import type {Metadata} from "next";
import {Geist, Geist_Mono} from "next/font/google"; import {Geist, Geist_Mono} from "next/font/google";
import "@/app/globals.css"; import "@/app/globals.css";
import React from "react"; import React from "react";
import {Header} from "@/components/Header/Header";
import {Footer} from "@/components/Footer";
const geistSans = Geist({ const geistSans = Geist({
variable: "--font-geist-sans", variable: "--font-geist-sans",
@@ -25,13 +27,17 @@ export default function RootLayout({
}>) { }>) {
return ( return (
<html lang="en" suppressHydrationWarning> <html lang="de" suppressHydrationWarning>
<body <body
className={`${geistSans.variable} ${geistMono.variable} antialiased`} className={`${geistSans.variable} ${geistMono.variable} antialiased flex flex-col min-h-screen`}
suppressHydrationWarning suppressHydrationWarning
> >
<a>OFFENES LAYOUT</a>
{children} <Header/>
<main className={"flex grow"}>
{children}
</main>
<Footer/>
</body> </body>
</html> </html>
); );

View File

@@ -1,12 +1,7 @@
'use client' 'use server'
import {SignInForm} from "@/components/SignInForm/SignInForm"; import {SignInForm} from "@/components/SignInForm/SignInForm";
interface Submission { export default async function Page() {
username: string;
password: string;
}
export default function Page() {
return ( return (
<SignInForm/> <SignInForm/>

View File

@@ -1,9 +1,8 @@
'use server' 'use server'
import {FormState} from '@/components/FormState/definitions' import {FormState} from '@/components/FormState/definitions'
import {ResponseToken} from "@/app/actions/interfaces"; import {ResponseToken} from "@/app/actions/interfaces";
import { cookies } from 'next/headers' import {cookies} from 'next/headers'
import 'server-only' import 'server-only'
import {encrypt} from "next/dist/server/app-render/encryption-utils";
import {redirect} from "next/dist/client/components/redirect"; import {redirect} from "next/dist/client/components/redirect";
export async function signup(state: FormState, formData: FormData) { export async function signup(state: FormState, formData: FormData) {
@@ -14,15 +13,15 @@ export async function signup(state: FormState, formData: FormData) {
if (!username || !password) { if (!username || !password) {
return { return {
errors: { errors: {
name: !username ? ["username not defined"]: undefined, name: !username ? ["username not defined"] : undefined,
message: !password ? ["password not defined"] : undefined, message: !password ? ["password not defined"] : undefined,
} }
} }
} }
console.log("Sign up", username, password) console.log("Sign up", username, password)
const endpoint: string = process.env.RUST_BACKEND_API_ENDPOINT as string; const endpoint: string = process.env.API_ENDPOINT as string;
// 3. Insert the user into the database or call an Auth Library's API // 3. Insert the user into the database or call an Auth Library's API
@@ -40,11 +39,11 @@ export async function signup(state: FormState, formData: FormData) {
if (!resp.ok) { if (!resp.ok) {
return { return {
errors: { errors: {
message: !resp.ok ? "Sign in unsuccessful" : undefined, message: "Sign in unsuccessful"
} }
} }
} }
const response = await resp.json() as ResponseToken; const response = await resp.json() as ResponseToken;
const expiresAt = new Date(Date.now() + 25 * 60 * 60 * 1000) const expiresAt = new Date(Date.now() + 25 * 60 * 60 * 1000)
@@ -52,8 +51,15 @@ export async function signup(state: FormState, formData: FormData) {
const cookieStore = await cookies() const cookieStore = await cookies()
cookieStore.set('token', response.token, { cookieStore.set('token', response.token, {
httpOnly: true, /*httpOnly: true,
secure: true, secure: true, */
expires: expiresAt,
sameSite: 'lax',
path: '/',
})
cookieStore.set('user', username, {
/*httpOnly: true,
secure: true, */
expires: expiresAt, expires: expiresAt,
sameSite: 'lax', sameSite: 'lax',
path: '/', path: '/',
@@ -62,9 +68,13 @@ export async function signup(state: FormState, formData: FormData) {
redirect("/home") redirect("/home")
} }
export async function sendRequestwToken(url: string,method: string, body?: JSON){ export async function sendRequestwToken(url: string, method: string, body?: string): Promise<Response | null> {
const cookieStore = await cookies(); const cookieStore = await cookies();
const token = cookieStore.get("token")?.value const token = cookieStore.get("token")?.value
const backendURL = process.env.API_ENDPOINT as string;
const fullUrl = `${backendURL}${url.startsWith("/") ? url: `/${url}`}`;
if (!token) return null; if (!token) return null;
@@ -75,9 +85,13 @@ export async function sendRequestwToken(url: string,method: string, body?: JSON)
"Authorization": bearerToken, "Authorization": bearerToken,
} }
return await fetch(url, { return await fetch(fullUrl, {
method, method,
headers, headers,
body: body ? JSON.stringify(body) : undefined, body: body ? body : undefined,
}) })
}
export async function sendRequestwTokenClient(url: string, method: string, body?: string) {
await sendRequestwToken(url, method, body)
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 206 KiB

View File

@@ -1,19 +1,11 @@
"use client" "use client"
import React from "react"; import React from "react";
import {ClassNameValue, twMerge} from "tailwind-merge" import {CustomButtonProps} from "@/components/Button";
import {clsx} from "clsx"; import {cn} from "@/components/cn";
interface CustomButtonProps extends React.HTMLProps<HTMLButtonElement> { export function Button({children, className, onClick, typeStyle = "primary", link, ...otherProps}: CustomButtonProps) {
children?: React.ReactNode
type?: "button" | "submit" | "reset"
typeStyle?: "primary" | "secondary" | "tertiary"
}
const cn = (...input: ClassNameValue[]) => twMerge(clsx(input)); const typStyleCss = () => {
export function Button({children, className, onClick, typeStyle = "primary", ...otherProps}: CustomButtonProps) {
let typStyleCss = () => {
switch (typeStyle) { switch (typeStyle) {
case "primary": case "primary":
return cn("bg-primary", "border-primary", "hover:border-tertiary"); return cn("bg-primary", "border-primary", "hover:border-tertiary");
@@ -35,10 +27,11 @@ export function Button({children, className, onClick, typeStyle = "primary", ...
typStyleCss(), typStyleCss(),
"transition-all", "transition-all",
"border-2", "border-2",
"p-1", "p-2 pr-8 pl-8",
"rounded", "rounded",
"m-2", "m-2",
"text-center", "text-center",
"text-2xl",
className, className,
onClick && "hover:cursor-pointer", onClick && "hover:cursor-pointer",
) )

View File

@@ -0,0 +1,2 @@
export * from './Button';
export * from './interfaces';

View File

@@ -0,0 +1,6 @@
export interface CustomButtonProps extends React.HTMLProps<HTMLButtonElement> {
children?: React.ReactNode
type?: "button" | "submit" | "reset"
typeStyle?: "primary" | "secondary" | "tertiary"
link?: string
}

View File

@@ -2,7 +2,7 @@
import 'server-only' import 'server-only'
import { cookies } from 'next/headers' import {cookies} from 'next/headers'
import {cache} from "react"; import {cache} from "react";
import {sendRequestwToken} from "@/app/actions/auth"; import {sendRequestwToken} from "@/app/actions/auth";
import {TokenIsValid} from "@/app/actions/interfaces"; import {TokenIsValid} from "@/app/actions/interfaces";
@@ -10,13 +10,13 @@ import {TokenIsValid} from "@/app/actions/interfaces";
export const verifySession = cache(async () => { export const verifySession = cache(async () => {
const cookie = (await cookies()).get('token')?.value const cookie = (await cookies()).get('token')?.value
const resp = await sendRequestwToken(`${process.env.API_ENDPOINT}/token/validate`,'GET') const resp = await sendRequestwToken(`/token/validate`, 'GET')
if (!cookie || resp === null) { if (!cookie || resp === null) {
return { isAuth: false, token: cookie } return {isAuth: false, token: cookie}
} }
console.log("hello world")
const is_valid = (await (resp as Response).json()) as TokenIsValid const is_valid = (await (resp as Response).json()) as TokenIsValid
return { isAuth: is_valid.is_valid, token: cookie } return {isAuth: is_valid.is_valid, token: cookie}
}) })

View File

@@ -0,0 +1,25 @@
'use server'
import {Link} from "@/components/Link";
export async function Footer() {
const date = new Date();
const year = date.getFullYear();
return (
<div
className={"border-t border-gray-100 flex items-center justify-between shadow-md transition-all mt-auto bottom-0 align-bottom"}>
<div className={"pl-4 flex flex-row ml-16"}>
<div className={"h-24"}></div>
<Link href={"/home"} typeStyle={"tertiary"} className={"text-xl h-min m-auto border-0"}>Home</Link>
<Link href={"/contact"} typeStyle={"tertiary"}
className={"text-xl h-min m-auto border-0"}>Kontakt</Link>
<Link href={"/privacy"} typeStyle={"tertiary"}
className={"text-xl h-min m-auto border-0"}>Datenschutzerklärung</Link>
</div>
<div className={"pl-4 flex flex-row mr-16"}>
© {year} Leander Mundkowsky
</div>
</div>
)
}

View File

@@ -0,0 +1 @@
export * from './Footer';

View File

@@ -1,15 +1,52 @@
'use client'
import Logo from "@/components/svg/logo"; import Logo from "@/components/svg/logo";
import {Notification} from "@/components/Notification";
import {Link} from "@/components/Link";
import {LogoutButton} from "@/components/LogoutButton";
import {useLoggedState} from "@/components/isLoggedIn";
import {getUser} from "@/components/getUser";
import {useEffect, useState} from "react";
export function Header() { export function Header() {
const [username, setUsername] = useState<string | undefined>(undefined);
useEffect(() => {
const execAsync = async () => {
const user = await getUser();
setUsername(user);
}
execAsync();
}, [])
const loggedState = useLoggedState();
return ( return (
<div className={"border-b border-gray-200 flex items-center justify-between shadow-md"}> <div className={"border-b border-gray-200 flex items-center justify-between shadow-md sticky transition-all"}>
<div className={"pl-4"}> <div className={"pl-4 flex flex-row ml-16"}>
<Logo className={"bg-primary p-4"} fill="white" width={"7dvh"} height={"7dvh"}/> <Logo className={"bg-primary p-4"} fill="transparent" width={"6rem"} height={"6rem"}/>
<div className={"w-20"}></div>
<Link href={"/home"} typeStyle={"tertiary"} className={"text-xl h-min m-auto border-0"}>Home</Link>
<Link href={"/tickets"} typeStyle={"tertiary"}
className={"text-xl h-min m-auto border-0"}>Tickets</Link>
<Link href={"/user"} typeStyle={"tertiary"}
className={"text-xl h-min m-auto border-0"}>Nutzer</Link>
</div> </div>
<div> <div>
<a className={"text-4xl"}></a>
</div> </div>
<div className={""}> <div className={"mr-16 flex flex-row"}>
{loggedState &&
(<>
<Notification/>
<LogoutButton/>
<div className={"m-auto"}>
<a>Angemeldet als {username}</a>
</div>
</>
)
}
</div> </div>
</div> </div>
) )

View File

@@ -0,0 +1,44 @@
'use client'
import React from "react";
import {cn} from "@/components/cn";
import {CustomLinkProps} from "@/components/Link/interfaces";
import NextLink from "next/link";
export function Link({children, className, typeStyle = "primary", href, ...otherProps}: CustomLinkProps) {
const typStyleCss = () => {
switch (typeStyle) {
case "primary":
return cn("bg-primary", "border-primary", "hover:border-tertiary");
case "secondary":
return cn("bg-primary/75", "border-primary/70", "hover:border-secondary");
case "tertiary":
return cn("border-tertiary", "hover:bg-primary/50", "hover:border-primary");
default:
break;
}
}
return (
<NextLink
href={href}
className={
cn(
typStyleCss(),
"transition-all",
"border-2",
"p-2 pr-8 pl-8",
"rounded",
"m-2",
"text-center",
"text-2xl",
className,
)
}
{...otherProps}
>
{children}
</NextLink>
)
}

View File

@@ -0,0 +1,2 @@
export * from './Link';
export * from './interfaces';

View File

@@ -0,0 +1,10 @@
import {LinkProps} from "next/link";
import React, {AnchorHTMLAttributes} from "react";
import {Url} from "next/dist/shared/lib/router/router";
export interface CustomLinkProps extends LinkProps, Omit<AnchorHTMLAttributes<HTMLAnchorElement>, keyof LinkProps> {
children?: React.ReactNode
type?: "button" | "submit" | "reset"
typeStyle?: "primary" | "secondary" | "tertiary"
link?: Url
}

View File

@@ -0,0 +1,36 @@
'use client'
import {Button} from "@/components/Button";
import {removeSession} from "@/components/LogoutButton/removeCookie";
import {useEffect, useState} from "react";
import Logout from "@/components/svg/logout";
import {refresh} from "next/cache";
export function LogoutButton() {
const [removeCookieState, setRemoveCookieState] = useState<boolean>(false);
useEffect(() => {
if (!removeCookieState) {
return;
}
console.log("Cleared Login")
const execAsync = async () => {
await removeSession();
setRemoveCookieState(false);
}
execAsync();
}, [removeCookieState])
function onClick() {
setRemoveCookieState(true);
}
return (
<Button onClick={onClick} className="m-auto w-min p-2 mr-2"><Logout/></Button>
)
}

View File

@@ -0,0 +1 @@
export * from "./LogoutButton"

View File

@@ -0,0 +1,14 @@
'use server'
import {cookies} from "next/headers";
import {redirect} from "next/dist/client/components/redirect";
export async function removeSession() {
const cookieStore = await cookies()
cookieStore.delete("token")
cookieStore.delete("user")
redirect("/login")
}

View File

@@ -0,0 +1,38 @@
'use client'
import {Button} from "@/components/Button";
import Bell from "@/components/svg/notifications/bell";
import {useState} from "react";
export function Notification() {
const [notificationIsActive, setNotificationIsActive] = useState<boolean>(false);
return (
/* 'relative' sorgt dafür, dass das absolute Menü hier verankert bleibt */
<div className="relative inline-block">
<Button
className="w-min p-2"
onClick={() => setNotificationIsActive(!notificationIsActive)}
>
<Bell/>
</Button>
{notificationIsActive && <NotificationMenu/>}
</div>
);
}
function NotificationMenu() {
return (
/* 'absolute' nimmt das Element aus dem Fluss.
'right-0' richtet es an der rechten Kante des Buttons aus.
'z-50' sorgt dafür, dass es über dem Footer/Content liegt.
*/
<div
className="absolute right-0 w-64 bg-white border-2 border-gray-200 transition shadow-lg rounded-md p-4 z-50">
<p className="text-sm font-semibold text-gray-700">Benachrichtigungen</p>
<hr className="my-2"/>
<div className="text-gray-600">Hello World</div>
</div>
);
}

View File

@@ -0,0 +1 @@
export * from './Notification';

View File

@@ -1,18 +1,25 @@
'use client'
import Form from "next/dist/client/form"; import Form from "next/dist/client/form";
import {useActionState} from "react"; import {useActionState} from "react";
import {signup} from "@/app/actions/auth"; import {signup} from "@/app/actions/auth";
import {Button} from "@/components/Button";
export function SignInForm() { export function SignInForm() {
const [state, action, pending] = useActionState(signup, undefined) const [state, action, pending] = useActionState(signup, undefined)
return ( return (
<> <Form action={action} className={'flex grow flex-col items-center justify-center'}>
<Form action={action} className={'flex flex-col items-center justify-center'}> <input id="username" type="text" required={true} name="username"
<input id="username" type="text" required={true} name="username" className={"bg-gray-800/50 rounded p-4 m-2"} placeholder="Username" /> className={"bg-primary/50 rounded p-4 m-2"} placeholder="Username"/>
{state?.errors?.name && <p>{state.errors.name}</p>} {state?.errors?.name && <p>{state.errors.name.map((item, i) => (
<input id="password" type="password" required={true} name="password" className={"bg-gray-800/50 rounded p-4 m-2"} placeholder="Password" /> <div key={i}>
<button disabled={pending} type="submit" className={"bg-gray-800/50 rounded p-4 m-2 hover:bg-gray-800 hover:text-white transition-all"}>Login</button> {item}
</Form> </div>
</> ))}</p>}
<input id="password" type="password" required={true} name="password"
className={"bg-primary/50 rounded p-4 m-2"} placeholder="Password"/>
<Button disabled={pending} type="submit">
Login
</Button>
</Form>
) )
} }

View File

@@ -0,0 +1,17 @@
'use client'
import React from "react";
import {cn} from "@/components/cn";
interface SingleCardProps extends React.HTMLProps<HTMLDivElement> {
children?: React.ReactNode
}
export function SingleCard({children, className, ...props}: SingleCardProps) {
return (
<div
className={cn(className, "rounded", "flex", "flex-row", "items-center", "bg-secondary/15", "justify-center", "min-w-10", "min-h-10", "m-2", "p-3")} {...props}>
{children}
</div>
)
}

View File

View File

@@ -0,0 +1,33 @@
'use client'
import {Ticket} from "@/components/Tickets";
import {useRouter} from "next/navigation";
import React from "react";
export function Row({
ticketID,
ticketname,
username,
status,
priority,
category
}: Ticket) {
const router = useRouter()
const handleTableClick = () => {
router.push(`/tickets/${ticketID}`, {})
};
const StateMapping = ["Offen","Bearbeitet","Abgeschlossen"]
const PrioMapping = ["Niedrig","Mittel","Hoch","Notfall"]
return (
<tr
onClick={handleTableClick}
className="text-center border-b h-16 hover:bg-primary/10 cursor-pointer transition-colors "
>
<td>{ticketname}</td>
<td>{`${StateMapping[status]}`}</td>
<td>{`${PrioMapping[priority]}`}</td>
<td>{category}</td>
<td>{username}</td>
</tr>
)
}

View File

@@ -0,0 +1,72 @@
import React from "react";
import {Row} from "@/components/TicketTable";
import {Ticket} from "@/components/Tickets";
import {getTickets} from "@/components/Tickets/getTickets";
export async function TicketTable() {
const tickets = await getTickets();
console.log(tickets);
const _mockData: Ticket[] = [{
"ticketID": 1,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack",
"description": "string"
},
{
"ticketID": 2,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack",
"description": "string"
},
{
"ticketID": 3,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack",
"description": "string"
},
{
"ticketID": 4,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack",
"description": "string"
}]
return (
<div className="flex flex-col w-full px-8">
<table className="w-full rounded bg-primary/25 overflow-hidden">
<thead className="bg-primary/25 border-b border-secondary">
<tr className="h-14">
<th scope="col">Titel</th>
<th scope="col">Status</th>
<th scope="col">Priorität</th>
<th scope="col">Kategorie</th>
<th scope="col">Erstellt von</th>
</tr>
</thead>
<tbody>
{tickets ? tickets.map((item, i) => (
<Row key={`${item.ticketname}-${i}`} ticketID={item.ticketID} ticketname={item.ticketname}
username={item.username}
status={item.status} priority={item.priority} category={item.category}/>
)): (<tr><td>There are no Tickets available</td></tr>)}
</tbody>
</table>
</div>
)
}

View File

@@ -0,0 +1,2 @@
export * from './TicketTable';
export * from "./Row"

View File

@@ -0,0 +1,18 @@
'use server'
import {sendRequestwToken} from "@/app/actions/auth";
export async function getCategories() : Promise<string[]| undefined> {
const result = await sendRequestwToken(`/categories`,"GET");
if (!(await result?.clone().text())) {
return undefined;
}
const ticket = (await result?.json()) as string[];
if (ticket === null || ticket === undefined) {
return undefined;
}
return ticket;
}

View File

@@ -0,0 +1,19 @@
'use server'
import {sendRequestwToken} from "@/app/actions/auth";
import {DetailedTicket} from "@/components/Tickets";
export async function getTicket(ticketId: number) : Promise<DetailedTicket| undefined> {
const result = await sendRequestwToken(`/ticket/show/${ticketId}`,"GET");
if (!(await result?.clone().text())) {
return undefined;
}
const ticket = (await result?.json()) as DetailedTicket;
if (ticket === null || ticket === undefined) {
return undefined;
}
return ticket;
}

View File

@@ -0,0 +1,21 @@
import {sendRequestwToken} from "@/app/actions/auth";
import {Ticket} from "@/components/Tickets/interfaces";
export async function getTickets() {
const result = await sendRequestwToken("/ticket/show/all","GET");
if (!(await result?.clone().text())) {
return undefined;
}
const tickets = await result?.json() as Ticket[];
console.log("/////////////////////////////")
console.dir(tickets);
console.log(tickets);
if (tickets === null || tickets === undefined) {
return undefined;
}
return tickets;
}

View File

@@ -0,0 +1,4 @@
export * from "./interfaces"
export * from "./getTicket"
export * from "./getTickets"
export * from "./getCategories"

View File

@@ -0,0 +1,23 @@
export interface Ticket {
"ticketID"?: number,
"status" : number,
"priority": number,
"category": string | undefined,
"ticketname": string,
description?: string,
"username": string,
}
export interface Messages {
"messageID": number,
"sequence": number,
"sendAt": string,
"content": string,
"sender": number,
"senderUsername": string,
}
export interface DetailedTicket extends Ticket {
userID: number,
messages: Messages[] | undefined,
}

View File

@@ -0,0 +1,30 @@
'use client'
import {Ticket} from "@/components/Tickets";
export function Row({
ticketID,
ticketname,
username,
status,
priority,
category
}: Ticket) {
const handleTableClick = (event: React.MouseEvent<HTMLTableRowElement>) => {
// Beispiel: ID aus einem Daten-Attribut auslesen
const target = event.currentTarget;
console.log("Zeile geklickt!");
};
return (
<tr
onClick={handleTableClick}
className="text-center border-b h-16 hover:bg-primary/10 cursor-pointer transition-colors"
>
<td>{ticketname}</td>
<td>{`${status}`}</td>
<td>{`${priority}`}</td>
<td>{category}</td>
<td>{username}</td>
</tr>
)
}

View File

@@ -0,0 +1,65 @@
import React from "react";
import {Row} from "@/components/TicketTable";
import {RequestTicket} from "@/components/RequestTickets";
export async function UserTable() {
const mockData: RequestTicket[] = [{
"ticketID": 1,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack"
},
{
"ticketID": 2,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack"
},
{
"ticketID": 3,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack"
},
{
"ticketID": 4,
"ticketname": "ADAQ",
"username": "admin",
"status": 1,
"priority": 5,
"category": "Crack"
}]
return (
<div className="flex flex-col w-full px-8">
{/* Hinweis: mx-8 statt ml/mr für saubere Zentrierung bei w-full */}
<table className="w-full rounded bg-primary/25 overflow-hidden">
<thead className="bg-primary/25 border-b border-secondary">
<tr className="h-14">
<th scope="col">Nutzer</th>
<th scope="col">Status</th>
<th scope="col">Priorität</th>
<th scope="col">Kategorie</th>
<th scope="col">Erstellt von</th>
</tr>
</thead>
<tbody>
{mockData.map((item, i) => (
<Row key={`${item.ticketID}-${i}`} ticketID={item.ticketID} ticketname={item.ticketname}
username={item.username}
status={item.status} priority={item.priority} category={item.category}/>
))}
</tbody>
</table>
</div>
)
}

View File

@@ -0,0 +1,2 @@
export * from './UserTable';
export * from "./Row"

4
src/components/cn/cn.tsx Normal file
View File

@@ -0,0 +1,4 @@
import {ClassNameValue, twMerge} from "tailwind-merge";
import {clsx} from "clsx";
export const cn = (...input: ClassNameValue[]) => twMerge(clsx(input));

View File

@@ -0,0 +1 @@
export * from "./cn"

View File

View File

@@ -0,0 +1,7 @@
'use server'
import {cookies} from "next/headers";
export async function getUser() {
const cookieStore = await cookies()
return cookieStore.get("user")?.value || undefined;
}

View File

@@ -0,0 +1 @@
export * from "./getUser"

View File

@@ -0,0 +1,2 @@
export * from "./isLoggedIn"
export * from "./isLoggedInServer"

View File

@@ -0,0 +1,26 @@
'use client'
import {useEffect, useState} from "react";
import {usePathname} from "next/navigation";
import {isLoggedInServer} from "@/components/isLoggedIn/isLoggedInServer";
/**
* checks if the user is logged in
*
* Updates every time a new Path is evaluated
*/
export function useLoggedState() {
const [isLoggedinVal, setIsLoggedIn] = useState<boolean>(false);
const path = usePathname()
useEffect(()=> {
const asyncReq = async () => {
const response = await isLoggedInServer()
setIsLoggedIn(response)
}
asyncReq()
},[path])
return isLoggedinVal
}

View File

@@ -0,0 +1,11 @@
'use server'
import {cookies} from "next/headers";
export async function isLoggedInServer() {
'use server'
const cookieStore = await cookies()
const token = cookieStore.get('token');
const user = cookieStore.get("user");
return !!(user && token);
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,17 @@
import * as React from "react"
import {SVGProps} from "react"
const Logout = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
fill="#FFF"
viewBox="0 -960 960 960"
{...props}
>
<path
d="M200-120q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h280v80H200v560h280v80H200Zm440-160-55-58 102-102H360v-80h327L585-622l55-58 200 200-200 200Z"/>
</svg>
)
export default Logout

View File

@@ -0,0 +1,17 @@
import * as React from "react"
import {SVGProps} from "react"
const Bell = (props: SVGProps<SVGSVGElement>) => (
<svg
xmlns="http://www.w3.org/2000/svg"
width={24}
height={24}
fill="#FFF"
viewBox="0 -960 960 960"
{...props}
>
<path
d="M180-204.62v-59.99h72.31v-298.47q0-80.69 49.81-142.69 49.8-62 127.88-79.31V-810q0-20.83 14.57-35.42Q459.14-860 479.95-860q20.82 0 35.43 14.58Q530-830.83 530-810v24.92q78.08 17.31 127.88 79.31 49.81 62 49.81 142.69v298.47H780v59.99H180ZM479.93-92.31q-29.85 0-51.04-21.24-21.2-21.24-21.2-51.07h144.62q0 29.93-21.26 51.12-21.26 21.19-51.12 21.19Z"/>
</svg>
)
export default Bell

View File

@@ -0,0 +1 @@
export * from "./bell"

View File

@@ -1,8 +1,9 @@
import {NextRequest, NextResponse} from "next/server"; import {NextRequest, NextResponse} from "next/server";
import {verifySession} from "@/components/DAL/dal";
export default async function proxy(req: NextRequest) { export default async function proxy(req: NextRequest) {
const auth_is_valid = true // (await verifySession()).isAuth; const auth_is_valid = (await verifySession()).isAuth;
const route_is_login = req.nextUrl.pathname === "/login" const route_is_login = req.nextUrl.pathname === "/login"
if (route_is_login && auth_is_valid) { // Redirect User to Home if Login already acquired if (route_is_login && auth_is_valid) { // Redirect User to Home if Login already acquired