diff --git a/ui/next.config.ts b/ui/next.config.ts index e9ffa30..cb651cd 100644 --- a/ui/next.config.ts +++ b/ui/next.config.ts @@ -1,7 +1,5 @@ import type { NextConfig } from "next"; -const nextConfig: NextConfig = { - /* config options here */ -}; +const nextConfig: NextConfig = {}; export default nextConfig; diff --git a/ui/package-lock.json b/ui/package-lock.json index 7225517..e7222cf 100644 --- a/ui/package-lock.json +++ b/ui/package-lock.json @@ -8,6 +8,15 @@ "name": "ui", "version": "0.1.0", "dependencies": { + "@auth0/nextjs-auth0": "^4.7.0", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@fontsource/inter": "^5.2.6", + "@mui/icons-material": "^7.1.2", + "@mui/material": "^7.1.2", + "@mui/material-nextjs": "^7.1.1", + "@mui/system": "^7.1.1", + "axios": "^1.10.0", "next": "15.3.4", "react": "^19.0.0", "react-dom": "^19.0.0" @@ -15,7 +24,7 @@ "devDependencies": { "@eslint/eslintrc": "^3", "@tailwindcss/postcss": "^4", - "@types/node": "^20", + "@types/node": "^20.19.1", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", @@ -51,6 +60,172 @@ "node": ">=6.0.0" } }, + "node_modules/@auth0/nextjs-auth0": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@auth0/nextjs-auth0/-/nextjs-auth0-4.7.0.tgz", + "integrity": "sha512-Q9R707LNPeV23NUgNc0QhkVD5g2L+fZTfZzH6q37vHlsEG6QVqwoXsteJlq7Os2689O1RjrHWPKX6kIjx1SP1A==", + "license": "MIT", + "dependencies": { + "@edge-runtime/cookies": "^5.0.1", + "@panva/hkdf": "^1.2.1", + "jose": "^5.9.6", + "oauth4webapi": "^3.1.2", + "swr": "^2.2.5" + }, + "peerDependencies": { + "next": "^14.2.25 || ^15.2.3", + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-0", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/generator": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.5.tgz", + "integrity": "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.27.5", + "@babel/types": "^7.27.3", + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", + "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", + "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.27.5", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.5.tgz", + "integrity": "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.3" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", + "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", + "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/parser": "^7.27.2", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.27.4", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.4.tgz", + "integrity": "sha512-oNcu2QbHqts9BtOWJosOVJapWjBDSxGCpFvikNR5TGDYDQf3JwpIoMzIKrvfoti93cLfPJEG4tH9SPVeyCGgdA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.27.3", + "@babel/parser": "^7.27.4", + "@babel/template": "^7.27.2", + "@babel/types": "^7.27.3", + "debug": "^4.3.1", + "globals": "^11.1.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse/node_modules/globals": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", + "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/@babel/types": { + "version": "7.27.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.6.tgz", + "integrity": "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@edge-runtime/cookies": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@edge-runtime/cookies/-/cookies-5.0.2.tgz", + "integrity": "sha512-Sd8LcWpZk/SWEeKGE8LT6gMm5MGfX/wm+GPnh1eBEtCpya3vYqn37wYknwAHw92ONoyyREl1hJwxV/Qx2DWNOg==", + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/@emnapi/core": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz", @@ -84,6 +259,152 @@ "tslib": "^2.4.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", @@ -238,6 +559,15 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fontsource/inter": { + "version": "5.2.6", + "resolved": "https://registry.npmjs.org/@fontsource/inter/-/inter-5.2.6.tgz", + "integrity": "sha512-CZs9S1CrjD0jPwsNy9W6j0BhsmRSQrgwlTNkgQXTsAeDRM42LBRLo3eo9gCzfH4GvV7zpyf78Ozfl773826csw==", + "license": "OFL-1.1", + "funding": { + "url": "https://github.com/sponsors/ayuhito" + } + }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -717,7 +1047,6 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -732,7 +1061,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -742,7 +1070,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -752,20 +1079,298 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mui/core-downloads-tracker": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.2.tgz", + "integrity": "sha512-0gLO1PvbJwSYe5ji021tGj6HFqrtEPMGKK4L1zWwRbhzrWWUumUJvMvJUsIgWQIYQsgOnhq9k2Fc1BxLGHDsAg==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.2.tgz", + "integrity": "sha512-slqJByDub7Y1UcokrM17BoMBMvn8n7daXFXVoTv0MEH5k3sHjmsH8ql/Mt3s9vQ20cORDr83UZ448TEGcbrXtw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.1.2", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.2.tgz", + "integrity": "sha512-Z5PYKkA6Kd8vS04zKxJNpwuvt6IoMwqpbidV7RCrRQQKwczIwcNcS8L6GnN4pzFYfEs+N9v6co27DmG07rcnoA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/core-downloads-tracker": "^7.1.2", + "@mui/system": "^7.1.1", + "@mui/types": "^7.4.3", + "@mui/utils": "^7.1.1", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.1.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.1.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material-nextjs": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@mui/material-nextjs/-/material-nextjs-7.1.1.tgz", + "integrity": "sha512-6/tjmViYMI7XIqDTqK+n4t5B07YfVDq72emdBy/o8FLHsV7u477Ro0Aago2MQu8FrBQWDvzvvRkynIb02GjDBQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/cache": "^11.11.0", + "@emotion/react": "^11.11.4", + "@emotion/server": "^11.11.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "next": "^13.0.0 || ^14.0.0 || ^15.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/cache": { + "optional": true + }, + "@emotion/server": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/@mui/private-theming": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.1.tgz", + "integrity": "sha512-M8NbLUx+armk2ZuaxBkkMk11ultnWmrPlN0Xe3jUEaBChg/mcxa5HWIWS1EE4DF36WRACaAHVAvyekWlDQf0PQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/utils": "^7.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.1.tgz", + "integrity": "sha512-R2wpzmSN127j26HrCPYVQ53vvMcT5DaKLoWkrfwUYq3cYytL6TQrCH8JBH3z79B6g4nMZZVoaXrxO757AlShaw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.1.tgz", + "integrity": "sha512-Kj1uhiqnj4Zo7PDjAOghtXJtNABunWvhcRU0O7RQJ7WOxeynoH6wXPcilphV8QTFtkKaip8EiNJRiCD+B3eROA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/private-theming": "^7.1.1", + "@mui/styled-engine": "^7.1.1", + "@mui/types": "^7.4.3", + "@mui/utils": "^7.1.1", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.3", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.3.tgz", + "integrity": "sha512-2UCEiK29vtiZTeLdS2d4GndBKacVyxGvReznGXGr+CzW/YhjIX+OHUdCIczZjzcRAgKBGmE9zCIgoV9FleuyRQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.1.tgz", + "integrity": "sha512-BkOt2q7MBYl7pweY2JWwfrlahhp+uGLR8S+EhiyRaofeRYUWL2YKbSGQvN4hgSN1i8poN0PaUiii1kEMrchvzg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/types": "^7.4.3", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.11", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.11.tgz", @@ -971,6 +1576,25 @@ "node": ">=12.4.0" } }, + "node_modules/@panva/hkdf": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@panva/hkdf/-/hkdf-1.2.1.tgz", + "integrity": "sha512-6oclG6Y3PiDFcoyk8srjLfVKyMfVCKJ27JwNPViuXziFpmdz+MZnZN/aKY0JGXgYuO/VghU0jcOAZgWXZ1Dmrw==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rtsao/scc": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", @@ -1318,11 +1942,22 @@ "undici-types": "~6.21.0" } }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.1.8", "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz", "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==", - "dev": true, "license": "MIT", "dependencies": { "csstype": "^3.0.2" @@ -1338,6 +1973,15 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.35.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.35.0.tgz", @@ -2144,6 +2788,12 @@ "node": ">= 0.4" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -2170,6 +2820,17 @@ "node": ">=4" } }, + "node_modules/axios": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.10.0.tgz", + "integrity": "sha512-/1xYAC4MP/HEG+3duIhFr4ZQXR4sQXOIe+o6sdqzeykGLx6Upp/1p8MHqhINOvGeP7xyNHe7tsiJByc4SSVUxw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, "node_modules/axobject-query": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", @@ -2180,6 +2841,21 @@ "node": ">= 0.4" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", @@ -2245,7 +2921,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2276,7 +2951,6 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -2335,6 +3009,15 @@ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", "license": "MIT" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/color": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", @@ -2380,6 +3063,18 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2387,6 +3082,28 @@ "dev": true, "license": "MIT" }, + "node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -2406,7 +3123,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "dev": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -2474,7 +3190,6 @@ "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2531,6 +3246,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-libc": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", @@ -2554,11 +3287,20 @@ "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": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.1", @@ -2590,6 +3332,21 @@ "node": ">=10.13.0" } }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/error-ex/node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, "node_modules/es-abstract": { "version": "1.24.0", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", @@ -2663,7 +3420,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2673,7 +3429,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -2711,7 +3466,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -2724,7 +3478,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", @@ -2771,7 +3524,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -3293,6 +4045,12 @@ "node": ">=8" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -3331,6 +4089,26 @@ "dev": true, "license": "ISC" }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, "node_modules/for-each": { "version": "0.3.5", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", @@ -3347,11 +4125,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/form-data": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.3.tgz", + "integrity": "sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3392,7 +4185,6 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "dev": true, "license": "MIT", "dependencies": { "call-bind-apply-helpers": "^1.0.2", @@ -3417,7 +4209,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "dev": true, "license": "MIT", "dependencies": { "dunder-proto": "^1.0.1", @@ -3505,7 +4296,6 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3584,7 +4374,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -3597,7 +4386,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "dev": true, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" @@ -3613,7 +4401,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3622,6 +4409,15 @@ "node": ">= 0.4" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3636,7 +4432,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", @@ -3779,7 +4574,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -4124,11 +4918,19 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/jose": { + "version": "5.10.0", + "resolved": "https://registry.npmjs.org/jose/-/jose-5.10.0.tgz", + "integrity": "sha512-s+3Al/p9g32Iq+oqXxkW//7jk2Vig6FF1CFqzVXoTUXt2qz89YWbL+OwS17NFYEvxC35n0FKeGO2LGYSxeM2Gg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -4144,6 +4946,18 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -4151,6 +4965,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -4477,6 +5297,12 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -4504,7 +5330,6 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -4527,7 +5352,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -4557,6 +5381,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4623,7 +5468,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -4749,11 +5593,19 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/oauth4webapi": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/oauth4webapi/-/oauth4webapi-3.5.3.tgz", + "integrity": "sha512-2bnHosmBLAQpXNBLOvaJMyMkr4Yya5ohE5Q9jqyxiN+aa7GFCzvDN1RRRMrp0NkfqRR2MTaQNkcSUCCjILD9oQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4944,7 +5796,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" @@ -4953,6 +5804,24 @@ "node": ">=6" } }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4977,9 +5846,17 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, "license": "MIT" }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5052,7 +5929,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -5060,6 +5936,12 @@ "react-is": "^16.13.1" } }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5116,9 +5998,24 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, + "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/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -5167,7 +6064,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -5188,7 +6084,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -5513,6 +6408,15 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5710,6 +6614,12 @@ } } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -5727,7 +6637,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5736,6 +6645,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", + "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/tailwindcss": { "version": "4.1.10", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz", @@ -6037,6 +6959,15 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6162,6 +7093,15 @@ "node": ">=18" } }, + "node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/ui/package.json b/ui/package.json index d0624a9..bc1a8a5 100644 --- a/ui/package.json +++ b/ui/package.json @@ -9,19 +9,28 @@ "lint": "next lint" }, "dependencies": { + "@auth0/nextjs-auth0": "^4.7.0", + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@fontsource/inter": "^5.2.6", + "@mui/icons-material": "^7.1.2", + "@mui/material": "^7.1.2", + "@mui/material-nextjs": "^7.1.1", + "@mui/system": "^7.1.1", + "axios": "^1.10.0", + "next": "15.3.4", "react": "^19.0.0", - "react-dom": "^19.0.0", - "next": "15.3.4" + "react-dom": "^19.0.0" }, "devDependencies": { - "typescript": "^5", - "@types/node": "^20", + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "@types/node": "^20.19.1", "@types/react": "^19", "@types/react-dom": "^19", - "@tailwindcss/postcss": "^4", - "tailwindcss": "^4", "eslint": "^9", "eslint-config-next": "15.3.4", - "@eslint/eslintrc": "^3" + "tailwindcss": "^4", + "typescript": "^5" } } diff --git a/ui/public/file.svg b/ui/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/ui/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/public/globe.svg b/ui/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/ui/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/webui/public/logo.png b/ui/public/logo.png similarity index 100% rename from webui/public/logo.png rename to ui/public/logo.png diff --git a/ui/public/next.svg b/ui/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/ui/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/public/vercel.svg b/ui/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/ui/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/public/window.svg b/ui/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/ui/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/ui/src/app/components/ClientLayoutEffect.tsx b/ui/src/app/components/ClientLayoutEffect.tsx new file mode 100644 index 0000000..d550be7 --- /dev/null +++ b/ui/src/app/components/ClientLayoutEffect.tsx @@ -0,0 +1,21 @@ +'use client'; + +import { useEffect } from 'react'; +import axios, { AxiosResponse } from 'axios'; + +export default function ClientLayoutEffect(): null { + useEffect(() => { + async function fetchData(): Promise { + try { + const res: AxiosResponse = await axios.get('/token'); + console.log('Token response:', res.data); + } catch (error) { + console.error('Token fetch error:', error); + } + } + + fetchData().then(() => console.log('Ok')); + }, []); + + return null; +} diff --git a/ui/src/app/components/ImprinkAppBar.tsx b/ui/src/app/components/ImprinkAppBar.tsx new file mode 100644 index 0000000..41b6108 --- /dev/null +++ b/ui/src/app/components/ImprinkAppBar.tsx @@ -0,0 +1,238 @@ +'use client'; + +import { + AppBar, + Avatar, + Box, + Button, + Divider, + IconButton, + Menu, + MenuItem, + Paper, + Toolbar, + Typography, + useMediaQuery, + useTheme as useMuiTheme, +} from '@mui/material'; +import { + Menu as MenuIcon, + Home, + PhotoLibrary, + ShoppingBag, + Store, + Dashboard, + AdminPanelSettings, + Api, + BugReport, +} from '@mui/icons-material'; +import { useUser } from '@auth0/nextjs-auth0'; +import {useState, MouseEvent, JSX} from 'react'; +import ThemeToggleButton from '@/app/components/theme/ThemeToggleButton'; +import useRoles from '@/app/components/hooks/useRoles'; +import { useTheme } from '@/app/components/theme/ThemeContext'; + +interface NavLink { + label: string; + href: string; + icon: JSX.Element; + show: boolean; +} + +export default function ImprinkAppBar() { + const { user, isLoading } = useUser(); + const { isMerchant, isAdmin } = useRoles(); + const [anchorEl, setAnchorEl] = useState(null); + const theme = useMuiTheme(); + const { isDarkMode, toggleTheme } = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); + + const handleMenuOpen = (event: MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + }; + + const navigationLinks: NavLink[] = [ + { label: 'Home', href: '/', icon: , show: true }, + { label: 'Gallery', href: '/gallery', icon: , show: true }, + { label: 'Orders', href: '/orders', icon: , show: true }, + { label: 'Merchant', href: '/merchant', icon: , show: isMerchant }, + ]; + + const adminLinks: NavLink[] = [ + { label: 'Dashboard', href: '/dashboard', icon: , show: isMerchant }, + { label: 'Admin', href: '/admin', icon: , show: isAdmin }, + { label: 'Swagger', href: '/swagger', icon: , show: isAdmin }, + { label: 'SEQ', href: '/seq', icon: , show: isAdmin }, + ]; + + const visibleLinks = navigationLinks.filter(link => link.show); + const visibleAdminLinks = adminLinks.filter(link => link.show); + + const renderDesktopNavigation = () => ( + + {visibleLinks.map(link => ( + + ))} + + ); + + const renderAdminBar = () => { + if (!visibleAdminLinks.length || isMobile) return null; + + return ( + + + + Admin Tools + + {visibleAdminLinks.map(link => ( + + ))} + + + ); + }; + + const renderMobileMenu = () => ( + <> + + + + + + {[...visibleLinks, ...visibleAdminLinks].map(link => ( + + {link.icon} + {link.label} + + ))} + {!isLoading && ( + <> + + {user ? ( + <> + e.preventDefault()}> + + {user.name} + + e.stopPropagation()}> + + Theme + + + + + + + Logout + + + ) : ( + <> + + Login + + + Sign Up + + + + Theme + {isDarkMode ? '🌙' : '☀️'} + + + + )} + + )} + + + ); + + return ( + <> + + + {isMobile && renderMobileMenu()} + + Imprink + + {!isMobile && ( + <> + {renderDesktopNavigation()} + + {isLoading ? ( + + Loading... + + ) : user ? ( + + + {user.name} + + + ) : ( + + + + + )} + + )} + + + {renderAdminBar()} + + ); +} \ No newline at end of file diff --git a/ui/src/app/components/hooks/useRoles.ts b/ui/src/app/components/hooks/useRoles.ts new file mode 100644 index 0000000..1f425dd --- /dev/null +++ b/ui/src/app/components/hooks/useRoles.ts @@ -0,0 +1,71 @@ +import { useState, useEffect } from 'react'; +import { useUser } from '@auth0/nextjs-auth0'; +import clientApi from '@/lib/clientApi'; + +interface RoleResponse { + roleName: string; +} + +export const useRoles = () => { + const { user } = useUser(); + const [roles, setRoles] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchUserRoles = async () => { + if (!user) { + setRoles([]); + setError(null); + return; + } + + setIsLoading(true); + setError(null); + + try { + const response = await clientApi.get('/users/me/roles'); + const userRoles = response.data.map(role => role.roleName.toLowerCase()); + setRoles(userRoles); + } catch (err) { + console.error('Failed to fetch user roles:', err); + setError(err as Error); + setRoles([]); + } finally { + setIsLoading(false); + } + }; + + fetchUserRoles().then(r => console.log(r)); + }, [user]); + + const hasRole = (roleName: string): boolean => { + return roles.includes(roleName.toLowerCase()); + }; + + const hasAnyRole = (roleNames: string[]): boolean => { + return roleNames.some(roleName => hasRole(roleName)); + }; + + const hasAllRoles = (roleNames: string[]): boolean => { + return roleNames.every(roleName => hasRole(roleName)); + }; + + const isMerchant = hasAnyRole(['merchant', 'admin']); + const isAdmin = hasRole('admin'); + const isCustomer = hasRole('customer'); + + return { + roles, + isLoading, + error, + hasRole, + hasAnyRole, + hasAllRoles, + isMerchant, + isAdmin, + isCustomer, + }; +}; + +export default useRoles; diff --git a/ui/src/app/components/theme/MuiThemeProvider.tsx b/ui/src/app/components/theme/MuiThemeProvider.tsx new file mode 100644 index 0000000..95a9f28 --- /dev/null +++ b/ui/src/app/components/theme/MuiThemeProvider.tsx @@ -0,0 +1,23 @@ +'use client'; + +import { ThemeProvider } from '@mui/material/styles'; +import CssBaseline from '@mui/material/CssBaseline'; +import { darkTheme } from './darkTheme'; +import { lightTheme } from './lightTheme'; +import { useTheme } from './ThemeContext'; +import { ReactNode } from 'react'; + +interface MuiThemeProviderProps { + children: ReactNode; +} + +export default function MuiThemeProvider({ children }: MuiThemeProviderProps) { + const { isDarkMode } = useTheme(); + + return ( + + + {children} + + ); +} diff --git a/ui/src/app/components/theme/ThemeContext.tsx b/ui/src/app/components/theme/ThemeContext.tsx new file mode 100644 index 0000000..ed0aad5 --- /dev/null +++ b/ui/src/app/components/theme/ThemeContext.tsx @@ -0,0 +1,74 @@ +'use client'; + +import React, { + createContext, + useContext, + useState, + useEffect, + ReactNode, +} from 'react'; + +interface ThemeContextType { + isDarkMode: boolean; + toggleTheme: () => void; +} + +const ThemeContext = createContext(undefined); + +interface ThemeContextProviderProps { + children: ReactNode; +} + +export function ThemeContextProvider({ children }: ThemeContextProviderProps) { + const [isDarkMode, setIsDarkMode] = useState(true); + const [isInitialized, setIsInitialized] = useState(false); + + useEffect(() => { + const savedTheme = localStorage.getItem('theme-preference'); + const systemPrefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + + const shouldBeDark = savedTheme ? savedTheme === 'dark' : systemPrefersDark; + + setIsDarkMode(shouldBeDark); + setIsInitialized(true); + }, []); + + useEffect(() => { + if (!isInitialized) return; + + const savedTheme = localStorage.getItem('theme-preference'); + if (savedTheme) return; + + const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)'); + const handleChange = (e: MediaQueryListEvent) => { + setIsDarkMode(e.matches); + }; + + mediaQuery.addEventListener('change', handleChange); + return () => mediaQuery.removeEventListener('change', handleChange); + }, [isInitialized]); + + const toggleTheme = () => { + const newTheme = !isDarkMode; + setIsDarkMode(newTheme); + localStorage.setItem('theme-preference', newTheme ? 'dark' : 'light'); + }; + + if (!isInitialized) { + return null; + } + + return ( + + {children} + + ); +} + +export function useTheme(): ThemeContextType { + const context = useContext(ThemeContext); + if (!context) { + throw new Error('useTheme must be used within a ThemeContextProvider'); + } + return context; +} \ No newline at end of file diff --git a/ui/src/app/components/theme/ThemeToggleButton.tsx b/ui/src/app/components/theme/ThemeToggleButton.tsx new file mode 100644 index 0000000..e40faa3 --- /dev/null +++ b/ui/src/app/components/theme/ThemeToggleButton.tsx @@ -0,0 +1,26 @@ +'use client'; + +import { IconButton } from '@mui/material'; +import { useTheme } from './ThemeContext'; +import {JSX} from "react"; + +export default function ThemeToggleButton(): JSX.Element { + const { isDarkMode, toggleTheme } = useTheme(); + + return ( + + {isDarkMode ? '🌙' : '☀️'} + + ); +} diff --git a/ui/src/app/components/theme/darkTheme.ts b/ui/src/app/components/theme/darkTheme.ts new file mode 100644 index 0000000..2afcf02 --- /dev/null +++ b/ui/src/app/components/theme/darkTheme.ts @@ -0,0 +1,276 @@ +'use client' + +import { createTheme, ThemeOptions } from '@mui/material/styles'; + +export const darkTheme: ThemeOptions = createTheme({ + palette: { + mode: 'dark', + primary: { + main: '#6366f1', + light: '#818cf8', + dark: '#4f46e5', + contrastText: '#ffffff', + }, + secondary: { + main: '#f59e0b', + light: '#fbbf24', + dark: '#d97706', + contrastText: '#000000', + }, + background: { + default: '#0f0f23', + paper: '#1a1a2e', + }, + text: { + primary: '#f8fafc', + secondary: '#cbd5e1', + }, + error: { + main: '#ef4444', + light: '#f87171', + dark: '#dc2626', + }, + warning: { + main: '#f59e0b', + light: '#fbbf24', + dark: '#d97706', + }, + info: { + main: '#06b6d4', + light: '#22d3ee', + dark: '#0891b2', + }, + success: { + main: '#10b981', + light: '#34d399', + dark: '#059669', + }, + divider: '#334155', + }, + typography: { + fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + h1: { + fontSize: '2.5rem', + fontWeight: 700, + lineHeight: 1.2, + letterSpacing: '-0.025em', + }, + h2: { + fontSize: '2rem', + fontWeight: 600, + lineHeight: 1.3, + letterSpacing: '-0.025em', + }, + h3: { + fontSize: '1.5rem', + fontWeight: 600, + lineHeight: 1.4, + letterSpacing: '-0.015em', + }, + h4: { + fontSize: '1.25rem', + fontWeight: 600, + lineHeight: 1.4, + }, + h5: { + fontSize: '1.125rem', + fontWeight: 600, + lineHeight: 1.5, + }, + h6: { + fontSize: '1rem', + fontWeight: 600, + lineHeight: 1.5, + }, + body1: { + fontSize: '1rem', + lineHeight: 1.6, + fontWeight: 400, + }, + body2: { + fontSize: '0.875rem', + lineHeight: 1.6, + fontWeight: 400, + }, + button: { + fontSize: '0.875rem', + fontWeight: 500, + textTransform: 'none', + letterSpacing: '0.025em', + }, + }, + shape: { + borderRadius: 12, + }, + shadows: [ + 'none', + '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)', + '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', + '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', + '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + '0 25px 50px -12px rgba(0, 0, 0, 0.25)', + ], + components: { + MuiCssBaseline: { + styleOverrides: { + body: { + scrollbarWidth: 'thin', + scrollbarColor: '#6366f1 #1a1a2e', + '&::-webkit-scrollbar': { + width: '8px', + }, + '&::-webkit-scrollbar-track': { + background: '#1a1a2e', + }, + '&::-webkit-scrollbar-thumb': { + background: '#6366f1', + borderRadius: '4px', + }, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + borderRadius: '8px', + padding: '10px 24px', + fontSize: '0.875rem', + fontWeight: 500, + boxShadow: 'none', + '&:hover': { + boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)', + transform: 'translateY(-1px)', + }, + '&:active': { + transform: 'translateY(0)', + }, + }, + contained: { + background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', + '&:hover': { + background: 'linear-gradient(135deg, #5b21b6 0%, #7c3aed 100%)', + }, + }, + outlined: { + borderWidth: '1.5px', + '&:hover': { + borderWidth: '1.5px', + backgroundColor: 'rgba(99, 102, 241, 0.08)', + }, + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: '8px', + backgroundColor: 'rgba(255, 255, 255, 0.02)', + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: '#6366f1', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: '#6366f1', + borderWidth: '2px', + }, + }, + }, + }, + }, + MuiCard: { + styleOverrides: { + root: { + background: 'linear-gradient(145deg, #1a1a2e 0%, #16213e 100%)', + border: '1px solid rgba(99, 102, 241, 0.1)', + backdropFilter: 'blur(20px)', + '&:hover': { + border: '1px solid rgba(99, 102, 241, 0.2)', + transform: 'translateY(-2px)', + boxShadow: '0 20px 40px rgba(0, 0, 0, 0.3)', + }, + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + backgroundImage: 'none', + backgroundColor: '#1a1a2e', + border: '1px solid rgba(99, 102, 241, 0.1)', + }, + }, + }, + MuiAppBar: { + styleOverrides: { + root: { + background: 'rgba(26, 26, 46, 0.8)', + backdropFilter: 'blur(20px)', + borderBottom: '1px solid rgba(99, 102, 241, 0.1)', + boxShadow: '0 8px 32px rgba(0, 0, 0, 0.3)', + }, + }, + }, + MuiDrawer: { + styleOverrides: { + paper: { + background: 'linear-gradient(180deg, #1a1a2e 0%, #16213e 100%)', + border: 'none', + borderRight: '1px solid rgba(99, 102, 241, 0.1)', + }, + }, + }, + MuiChip: { + styleOverrides: { + root: { + background: 'rgba(99, 102, 241, 0.1)', + color: '#818cf8', + border: '1px solid rgba(99, 102, 241, 0.2)', + '&:hover': { + background: 'rgba(99, 102, 241, 0.2)', + }, + }, + }, + }, + MuiTab: { + styleOverrides: { + root: { + textTransform: 'none', + fontWeight: 500, + fontSize: '0.875rem', + '&.Mui-selected': { + color: '#6366f1', + }, + }, + }, + }, + MuiTabs: { + styleOverrides: { + indicator: { + background: 'linear-gradient(90deg, #6366f1, #8b5cf6)', + height: '3px', + borderRadius: '3px', + }, + }, + }, + }, +}); \ No newline at end of file diff --git a/ui/src/app/components/theme/lightTheme.ts b/ui/src/app/components/theme/lightTheme.ts new file mode 100644 index 0000000..7b7f812 --- /dev/null +++ b/ui/src/app/components/theme/lightTheme.ts @@ -0,0 +1,276 @@ +'use client' + +import { createTheme, ThemeOptions } from '@mui/material/styles'; + +export const lightTheme : ThemeOptions = createTheme({ + palette: { + mode: 'light', + primary: { + main: '#6366f1', + light: '#818cf8', + dark: '#4f46e5', + contrastText: '#ffffff', + }, + secondary: { + main: '#f59e0b', + light: '#fbbf24', + dark: '#d97706', + contrastText: '#000000', + }, + background: { + default: '#f8fafc', + paper: '#ffffff', + }, + text: { + primary: '#0f172a', + secondary: '#475569', + }, + error: { + main: '#ef4444', + light: '#f87171', + dark: '#dc2626', + }, + warning: { + main: '#f59e0b', + light: '#fbbf24', + dark: '#d97706', + }, + info: { + main: '#06b6d4', + light: '#22d3ee', + dark: '#0891b2', + }, + success: { + main: '#10b981', + light: '#34d399', + dark: '#059669', + }, + divider: '#e2e8f0', + }, + typography: { + fontFamily: '"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif', + h1: { + fontSize: '2.5rem', + fontWeight: 700, + lineHeight: 1.2, + letterSpacing: '-0.025em', + }, + h2: { + fontSize: '2rem', + fontWeight: 600, + lineHeight: 1.3, + letterSpacing: '-0.025em', + }, + h3: { + fontSize: '1.5rem', + fontWeight: 600, + lineHeight: 1.4, + letterSpacing: '-0.015em', + }, + h4: { + fontSize: '1.25rem', + fontWeight: 600, + lineHeight: 1.4, + }, + h5: { + fontSize: '1.125rem', + fontWeight: 600, + lineHeight: 1.5, + }, + h6: { + fontSize: '1rem', + fontWeight: 600, + lineHeight: 1.5, + }, + body1: { + fontSize: '1rem', + lineHeight: 1.6, + fontWeight: 400, + }, + body2: { + fontSize: '0.875rem', + lineHeight: 1.6, + fontWeight: 400, + }, + button: { + fontSize: '0.875rem', + fontWeight: 500, + textTransform: 'none', + letterSpacing: '0.025em', + }, + }, + shape: { + borderRadius: 12, + }, + shadows: [ + 'none', + '0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px 0 rgba(0, 0, 0, 0.06)', + '0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06)', + '0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05)', + '0 20px 25px -5px rgba(0, 0, 0, 0.1), 0 10px 10px -5px rgba(0, 0, 0, 0.04)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + '0 25px 50px -12px rgba(0, 0, 0, 0.15)', + ], + components: { + MuiCssBaseline: { + styleOverrides: { + body: { + scrollbarWidth: 'thin', + scrollbarColor: '#6366f1 #f1f5f9', + '&::-webkit-scrollbar': { + width: '8px', + }, + '&::-webkit-scrollbar-track': { + background: '#f1f5f9', + }, + '&::-webkit-scrollbar-thumb': { + background: '#6366f1', + borderRadius: '4px', + }, + }, + }, + }, + MuiButton: { + styleOverrides: { + root: { + borderRadius: '8px', + padding: '10px 24px', + fontSize: '0.875rem', + fontWeight: 500, + boxShadow: 'none', + '&:hover': { + boxShadow: '0 4px 12px rgba(99, 102, 241, 0.3)', + transform: 'translateY(-1px)', + }, + '&:active': { + transform: 'translateY(0)', + }, + }, + contained: { + background: 'linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%)', + '&:hover': { + background: 'linear-gradient(135deg, #5b21b6 0%, #7c3aed 100%)', + }, + }, + outlined: { + borderWidth: '1.5px', + '&:hover': { + borderWidth: '1.5px', + backgroundColor: 'rgba(99, 102, 241, 0.08)', + }, + }, + }, + }, + MuiTextField: { + styleOverrides: { + root: { + '& .MuiOutlinedInput-root': { + borderRadius: '8px', + backgroundColor: 'rgba(241, 245, 249, 0.5)', + '&:hover .MuiOutlinedInput-notchedOutline': { + borderColor: '#6366f1', + }, + '&.Mui-focused .MuiOutlinedInput-notchedOutline': { + borderColor: '#6366f1', + borderWidth: '2px', + }, + }, + }, + }, + }, + MuiCard: { + styleOverrides: { + root: { + background: 'linear-gradient(145deg, #ffffff 0%, #f8fafc 100%)', + border: '1px solid rgba(99, 102, 241, 0.1)', + backdropFilter: 'blur(20px)', + '&:hover': { + border: '1px solid rgba(99, 102, 241, 0.2)', + transform: 'translateY(-2px)', + boxShadow: '0 20px 40px rgba(99, 102, 241, 0.15)', + }, + transition: 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)', + }, + }, + }, + MuiPaper: { + styleOverrides: { + root: { + backgroundImage: 'none', + backgroundColor: '#ffffff', + border: '1px solid rgba(99, 102, 241, 0.1)', + }, + }, + }, + MuiAppBar: { + styleOverrides: { + root: { + background: 'rgba(255, 255, 255, 0.8)', + backdropFilter: 'blur(20px)', + borderBottom: '1px solid rgba(99, 102, 241, 0.1)', + boxShadow: '0 8px 32px rgba(99, 102, 241, 0.1)', + color: '#0f172a', + }, + }, + }, + MuiDrawer: { + styleOverrides: { + paper: { + background: 'linear-gradient(180deg, #ffffff 0%, #f8fafc 100%)', + border: 'none', + borderRight: '1px solid rgba(99, 102, 241, 0.1)', + }, + }, + }, + MuiChip: { + styleOverrides: { + root: { + background: 'rgba(99, 102, 241, 0.7)', + border: '1px solid rgba(99, 102, 241, 0.2)', + '&:hover': { + background: 'rgba(99, 102, 241, 0.9)', + }, + }, + }, + }, + MuiTab: { + styleOverrides: { + root: { + textTransform: 'none', + fontWeight: 500, + fontSize: '0.875rem', + '&.Mui-selected': { + color: '#6366f1', + }, + }, + }, + }, + MuiTabs: { + styleOverrides: { + indicator: { + background: 'linear-gradient(90deg, #6366f1, #8b5cf6)', + height: '3px', + borderRadius: '3px', + }, + }, + }, + }, +}); \ No newline at end of file diff --git a/ui/src/app/favicon.ico b/ui/src/app/favicon.ico index 718d6fe..b0ae2d4 100644 Binary files a/ui/src/app/favicon.ico and b/ui/src/app/favicon.ico differ diff --git a/ui/src/app/form/page.tsx b/ui/src/app/form/page.tsx new file mode 100644 index 0000000..e69de29 diff --git a/ui/src/app/gallery/page.tsx b/ui/src/app/gallery/page.tsx new file mode 100644 index 0000000..ff70fb1 --- /dev/null +++ b/ui/src/app/gallery/page.tsx @@ -0,0 +1,720 @@ +'use client'; + +import { + Box, + Container, + Typography, + Button, + Card, + CardContent, + CardMedia, + Grid, + Chip, + CircularProgress, + Alert, + TextField, + InputAdornment, + Pagination, + FormControl, + Select, + MenuItem, + InputLabel, + Drawer, + List, + ListItem, + ListItemButton, + ListItemText, + Collapse, + IconButton, + useMediaQuery, + useTheme, + Paper, + Slider, + Switch, + FormControlLabel, + SelectChangeEvent +} from '@mui/material'; +import {useState, useEffect, useCallback, KeyboardEvent, ChangeEvent, JSX} from 'react'; +import { + Search, + ExpandLess, + ExpandMore, + Close as CloseIcon, + Tune as TuneIcon +} from '@mui/icons-material'; +import clientApi from "@/lib/clientApi"; +import useRoles from "@/app/components/hooks/useRoles"; + +interface SortOption { + value: string; + label: string; +} + +interface Category { + id: string; + name: string; + parentCategoryId?: string; +} + +interface Product { + id: string; + name: string; + description: string; + basePrice: number; + imageUrl?: string; + isCustomizable: boolean; + category?: Category; +} + +interface ProductsResponse { + items: Product[]; + totalPages: number; + totalCount: number; +} + +interface Filters { + pageNumber: number; + pageSize: number; + searchTerm: string; + categoryId: string; + minPrice: number; + maxPrice: number; + isActive: boolean; + isCustomizable: boolean | null; + sortBy: string; + sortDirection: string; +} + +interface ApiParams { + PageNumber: number; + PageSize: number; + IsActive: boolean; + SortBy: string; + SortDirection: string; + SearchTerm?: string; + CategoryId?: string; + MinPrice?: number; + MaxPrice?: number; + IsCustomizable?: boolean; +} + +const SORT_OPTIONS: SortOption[] = [ + { value: 'Name-ASC', label: 'Name (A-Z)' }, + { value: 'Name-DESC', label: 'Name (Z-A)' }, + { value: 'Price-ASC', label: 'Price (Low to High)' }, + { value: 'Price-DESC', label: 'Price (High to Low)' }, + { value: 'CreatedDate-DESC', label: 'Newest First' }, + { value: 'CreatedDate-ASC', label: 'Oldest First' } +]; + +const PAGE_SIZE_OPTIONS: number[] = [12, 24, 48, 96]; + +export default function GalleryPage(): JSX.Element { + const theme = useTheme(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); + const { isAdmin } = useRoles(); + + const heightOffset = isAdmin ? 192 : 128; + + const [products, setProducts] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [totalPages, setTotalPages] = useState(0); + const [totalCount, setTotalCount] = useState(0); + + const [categories, setCategories] = useState([]); + const [categoriesLoading, setCategoriesLoading] = useState(true); + const [expandedCategories, setExpandedCategories] = useState>(new Set()); + + const [filters, setFilters] = useState({ + pageNumber: 1, + pageSize: 24, + searchTerm: '', + categoryId: '', + minPrice: 0, + maxPrice: 1000, + isActive: true, + isCustomizable: null, + sortBy: 'Name', + sortDirection: 'ASC' + }); + + const [mobileDrawerOpen, setMobileDrawerOpen] = useState(false); + const [priceRange, setPriceRange] = useState([0, 1000]); + const [searchInput, setSearchInput] = useState(''); + + useEffect(() => { + const fetchCategories = async (): Promise => { + try { + const response = await clientApi.get('/products/categories'); + setCategories(response.data); + } catch (err) { + console.error('Error fetching categories:', err); + } finally { + setCategoriesLoading(false); + } + }; + + fetchCategories().then(r => console.log(r)); + }, []); + + const fetchProducts = useCallback(async (): Promise => { + setLoading(true); + try { + const params: ApiParams = { + PageNumber: filters.pageNumber, + PageSize: filters.pageSize, + IsActive: filters.isActive, + SortBy: filters.sortBy, + SortDirection: filters.sortDirection + }; + + if (filters.searchTerm) params.SearchTerm = filters.searchTerm; + if (filters.categoryId) params.CategoryId = filters.categoryId; + if (filters.minPrice > 0) params.MinPrice = filters.minPrice; + if (filters.maxPrice < 1000) params.MaxPrice = filters.maxPrice; + if (filters.isCustomizable !== null) params.IsCustomizable = filters.isCustomizable; + + const response = await clientApi.get('/products/', { params }); + + setProducts(response.data.items); + setTotalPages(response.data.totalPages); + setTotalCount(response.data.totalCount); + } catch (err) { + setError('Failed to load products'); + console.error('Error fetching products:', err); + } finally { + setLoading(false); + } + }, [filters]); + + useEffect(() => { + fetchProducts(); + }, [fetchProducts]); + + const handleFilterChange = (key: K, value: Filters[K]): void => { + setFilters(prev => ({ + ...prev, + [key]: value, + pageNumber: key !== 'pageNumber' ? 1 : (value as number) + })); + }; + + const handleSearch = (): void => { + handleFilterChange('searchTerm', searchInput); + }; + + const handlePriceRangeChange = (event: Event, newValue: number | number[]): void => { + setPriceRange(newValue as number[]); + }; + + const handlePriceRangeCommitted = (event: Event | React.SyntheticEvent, newValue: number | number[]): void => { + const range = newValue as number[]; + handleFilterChange('minPrice', range[0]); + handleFilterChange('maxPrice', range[1]); + }; + + const handleSortChange = (value: string): void => { + const [sortBy, sortDirection] = value.split('-'); + handleFilterChange('sortBy', sortBy); + handleFilterChange('sortDirection', sortDirection); + }; + + const toggleCategoryExpansion = (categoryId: string): void => { + const newExpanded = new Set(expandedCategories); + if (newExpanded.has(categoryId)) { + newExpanded.delete(categoryId); + } else { + newExpanded.add(categoryId); + } + setExpandedCategories(newExpanded); + }; + + const getChildCategories = (parentId: string): Category[] => { + return categories.filter(cat => cat.parentCategoryId === parentId); + }; + + const getParentCategories = (): Category[] => { + return categories.filter(cat => !cat.parentCategoryId); + }; + + const CategorySidebar = (): JSX.Element => ( + + + Categories + + + + + + handleFilterChange('categoryId', '')} + sx={{ borderRadius: 1, mb: 0.5 }} + > + + + + + {getParentCategories().map((category) => { + const childCategories = getChildCategories(category.id); + const hasChildren = childCategories.length > 0; + const isExpanded = expandedCategories.has(category.id); + + return ( + + + handleFilterChange('categoryId', category.id)} + sx={{ borderRadius: 1, mb: 0.5 }} + > + + {hasChildren && ( + { + e.stopPropagation(); + toggleCategoryExpansion(category.id); + }} + > + {isExpanded ? : } + + )} + + + + {hasChildren && ( + + + {childCategories.map((childCategory) => ( + + handleFilterChange('categoryId', childCategory.id)} + sx={{ borderRadius: 1, mb: 0.5 }} + > + + + + ))} + + + )} + + ); + })} + + + + + + Price Range + + + `$${value}`} + /> + + ${priceRange[0]} + ${priceRange[1]} + + + + ) => + handleFilterChange('isCustomizable', e.target.checked ? true : null) + } + /> + } + label="Customizable Only" + sx={{ mb: 0 }} + /> + + + ); + + return ( + + + {!isMobile && ( + + + {categoriesLoading ? ( + + + + ) : ( + + )} + + + )} + + + + + + Product Gallery + + + Explore our complete collection of customizable products + + + + + + {isMobile && ( + + setMobileDrawerOpen(true)} + sx={{ mr: 1 }} + size="small" + > + + + + )} + + + ) => setSearchInput(e.target.value)} + onKeyPress={(e: KeyboardEvent) => e.key === 'Enter' && handleSearch()} + size={isMobile ? "small" : "medium"} + InputProps={{ + startAdornment: ( + + + + ), + endAdornment: searchInput && ( + + + + + + ) + }} + /> + + + + + Sort By + + + + + + + Per Page + + + + + + + Showing {products.length} of {totalCount} products + + + + + + {loading && ( + + + + )} + + {error && ( + + {error} + + )} + + {!loading && !error && products.length === 0 && ( + + + No products found + + + Try adjusting your search criteria or filters + + + )} + + {!loading && !error && products.length > 0 && ( + + {products.map((product) => ( + + + + + {product.name} + + + {product.category && ( + + )} + + + {product.description} + + + + + From ${product.basePrice?.toFixed(2)} + + {product.isCustomizable && ( + + )} + + + + + + ))} + + )} + + {!loading && !error && totalPages > 1 && ( + + handleFilterChange('pageNumber', page)} + color="primary" + size={isMobile ? 'small' : 'large'} + showFirstButton={!isMobile} + showLastButton={!isMobile} + /> + + )} + + + + + setMobileDrawerOpen(false)} + ModalProps={{ keepMounted: true }} + PaperProps={{ + sx: { + width: 280, + display: 'flex', + flexDirection: 'column' + } + }} + > + + + Filters + + setMobileDrawerOpen(false)}> + + + + {categoriesLoading ? ( + + + + ) : ( + + )} + + + ); +} \ No newline at end of file diff --git a/ui/src/app/globals.css b/ui/src/app/globals.css deleted file mode 100644 index a2dc41e..0000000 --- a/ui/src/app/globals.css +++ /dev/null @@ -1,26 +0,0 @@ -@import "tailwindcss"; - -:root { - --background: #ffffff; - --foreground: #171717; -} - -@theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); -} - -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; - } -} - -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; -} diff --git a/ui/src/app/layout.tsx b/ui/src/app/layout.tsx index 2b7599d..8a723f5 100644 --- a/ui/src/app/layout.tsx +++ b/ui/src/app/layout.tsx @@ -1,35 +1,37 @@ -import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; -import "./globals.css"; -import React from "react"; +import { Inter } from 'next/font/google'; +import { Metadata } from 'next'; +import { ReactNode } from 'react'; +import MuiThemeProvider from './components/theme/MuiThemeProvider'; +import { ThemeContextProvider } from './components/theme/ThemeContext'; +import { AppRouterCacheProvider } from "@mui/material-nextjs/v13-appRouter"; +import ImprinkAppBar from "@/app/components/ImprinkAppBar"; +import ClientLayoutEffect from "@/app/components/ClientLayoutEffect"; -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); +const inter = Inter({ subsets: ['latin'] }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: 'Imprink', + description: 'Turn your dreams into colorful realities!', }; -export default function RootLayout({ - children, -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - ); +interface RootLayoutProps { + children: ReactNode; } + +export default function RootLayout({ children }: RootLayoutProps) { + return ( + + + + + + + + {children} + + + + + + ); +} \ No newline at end of file diff --git a/ui/src/app/page.tsx b/ui/src/app/page.tsx index e68abe6..4b3a205 100644 --- a/ui/src/app/page.tsx +++ b/ui/src/app/page.tsx @@ -1,103 +1,447 @@ -import Image from "next/image"; +'use client'; -export default function Home() { - return ( -
-
- Next.js logo -
    -
  1. - Get started by editing{" "} - - src/app/page.tsx - - . -
  2. -
  3. - Save and see your changes instantly. -
  4. -
+import { + Box, + Container, + Typography, + Button, + Card, + CardContent, + CardMedia, + Grid, + Chip, + CircularProgress, + Alert +} from '@mui/material'; +import {useState, useEffect, JSX} from 'react'; +import { ShoppingCart, Palette, ImageOutlined, CreditCard, LocalShipping, CheckCircle } from '@mui/icons-material'; +import clientApi from "@/lib/clientApi"; - -
- -
- ); +interface Product { + id: string; + name: string; + description: string; + imageUrl?: string; + basePrice: number; + isCustomizable: boolean; } + +interface ApiResponse { + items: Product[]; +} + +interface Step { + number: number; + label: string; + description: string; + icon: JSX.Element; + details: string; +} + +export default function HomePage(): JSX.Element { + const [products, setProducts] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + + useEffect(() => { + const fetchProducts = async (): Promise => { + try { + const response = await clientApi.get('/products/', { + params: { + PageSize: 3, + PageNumber: 1, + IsActive: true, + SortBy: 'Price', + SortDirection: 'DESC' + } + }); + setProducts(response.data.items); + } catch (err) { + setError('Failed to load products'); + console.error('Error fetching products:', err); + } finally { + setLoading(false); + } + }; + + fetchProducts(); + }, []); + + const steps: Step[] = [ + { + number: 1, + label: 'Pick an Item', + description: 'Browse our extensive collection of customizable products and select the perfect base for your design. From premium t-shirts and hoodies to mugs, phone cases, and more - we have everything you need to bring your vision to life.', + icon: , + details: 'Explore hundreds of high-quality products across multiple categories. Filter by material, size, color, and price to find exactly what you\'re looking for.' + }, + { + number: 2, + label: 'Choose Variant', + description: 'Select from available sizes, colors, and material options that match your preferences and needs. Each product comes with detailed specifications and sizing guides.', + icon: , + details: 'View real-time previews of different variants. Check material quality, durability ratings, and care instructions for each option.' + }, + { + number: 3, + label: 'Customize with Images', + description: 'Upload your own designs, add custom text, or use our intuitive design tools to create something truly unique. Our editor supports various file formats and offers professional design features.', + icon: , + details: 'Drag and drop images, adjust positioning, add filters, create text overlays, and preview your design in real-time on the selected product.' + }, + { + number: 4, + label: 'Pay', + description: 'Complete your order with our secure checkout process. We accept multiple payment methods and provide instant order confirmation with detailed receipts.', + icon: , + details: 'Review your design, confirm quantities, apply discount codes, and choose from various secure payment options including cards, PayPal, and more.' + }, + { + number: 5, + label: 'Wait for Order', + description: 'Sit back and relax while we handle the rest. Our professional printing team will carefully produce your custom item and ship it directly to your door.', + icon: , + details: 'Track your order status in real-time, from printing to packaging to shipping. Receive updates via email and SMS throughout the process.' + } + ]; + + return ( + + + + + + Custom Printing
+ Made Simple +
+ + Transform your ideas into reality with our premium custom printing services. + From t-shirts to mugs, we bring your designs to life with professional quality + and lightning-fast turnaround times. + + + + + + + + + Professional Quality Guaranteed + + + + + + + + Fast 24-48 Hour Turnaround + + + + + + + + Free Design Support + + + + + + + + 100% Satisfaction Promise + + + + + + + + + + + + ⭐⭐⭐⭐⭐ Trusted by 10,000+ customers • 4.9/5 rating + +
+
+
+ + + + + Featured Products + + + Discover our most popular customizable products. Each item is carefully selected + for quality and perfect for personalization. + + + + {loading && ( + + + + )} + + {error && ( + + {error} + + )} + + {!loading && !error && ( + + {products.map((product: Product) => ( + + + + + {product.name} + + + {product.description} + + + + From ${product.basePrice?.toFixed(2)} + + {product.isCustomizable && ( + + )} + + + + + ))} + + )} + + + + + + + How It Works + + + Our streamlined process makes custom printing simple and stress-free. + Follow these five easy steps to get your perfect custom products. + + + + + {steps.map((step: Step, index: number) => ( + + + + + + + {step.icon} + + + {step.number} + + + + + + + + {step.label} + + + {step.description} + + + {step.details} + + + + + + + ))} + + + + + + + Ready to Get Started? + + + Join thousands of satisfied customers who trust us with their custom printing needs. + Quality guaranteed, fast turnaround, and competitive prices. + + + +
+ ); +} \ No newline at end of file diff --git a/ui/src/app/token/route.ts b/ui/src/app/token/route.ts new file mode 100644 index 0000000..53d8e52 --- /dev/null +++ b/ui/src/app/token/route.ts @@ -0,0 +1,23 @@ +import { NextResponse } from 'next/server'; +import { auth0 } from '@/lib/auth0'; +import serverApi from '@/lib/serverApi'; + +export async function GET(): Promise { + try { + const session = await auth0.getSession(); + const token = session?.tokenSet?.accessToken; + + if (!token) { + return NextResponse.json({ error: 'No access token found' }, { status: 401 }); + } + + await serverApi.post('/users/me/sync', null, { + headers: { Authorization: `Bearer ${token}` } + }); + + return NextResponse.json("Ok"); + } catch (error) { + console.error('Error in /serverApi/token:', error); + return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 }); + } +} diff --git a/ui/src/lib/auth0.ts b/ui/src/lib/auth0.ts new file mode 100644 index 0000000..866f6f4 --- /dev/null +++ b/ui/src/lib/auth0.ts @@ -0,0 +1,8 @@ +import { Auth0Client } from "@auth0/nextjs-auth0/server"; + +export const auth0 = new Auth0Client({ + authorizationParameters: { + scope: process.env.AUTH0_SCOPE, + audience: process.env.AUTH0_AUDIENCE + } +}); \ No newline at end of file diff --git a/ui/src/lib/clientApi.ts b/ui/src/lib/clientApi.ts new file mode 100644 index 0000000..c33ee82 --- /dev/null +++ b/ui/src/lib/clientApi.ts @@ -0,0 +1,36 @@ +import axios, { InternalAxiosRequestConfig } from 'axios'; + +const clientApi = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, + withCredentials: true, +}); + +clientApi.interceptors.request.use( + async (config: InternalAxiosRequestConfig): Promise => { + if (typeof window === 'undefined') return config; + + try { + const res = await fetch('/auth/access-token'); + + if (!res.ok) { + console.error('Failed to fetch token'); + return config; + } + + const data: { token?: string } = await res.json(); + + if (data.token) { + config.headers.set('Authorization', `Bearer ${data.token}`); + } else { + console.warn('No token received from /auth/access-token'); + } + } catch (err) { + console.error('Error fetching token:', err); + } + + return config; + }, + (error) => Promise.reject(error) +); + +export default clientApi; diff --git a/ui/src/lib/serverApi.ts b/ui/src/lib/serverApi.ts new file mode 100644 index 0000000..5371f2f --- /dev/null +++ b/ui/src/lib/serverApi.ts @@ -0,0 +1,8 @@ +import axios from "axios"; + +const serverApi = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL, + withCredentials: true, +}); + +export default serverApi; \ No newline at end of file diff --git a/ui/src/middleware.ts b/ui/src/middleware.ts new file mode 100644 index 0000000..234cf75 --- /dev/null +++ b/ui/src/middleware.ts @@ -0,0 +1,12 @@ +import { auth0 } from './lib/auth0'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function middleware(request: NextRequest): Promise { + return await auth0.middleware(request); +} + +export const config = { + matcher: [ + '/((?!_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)', + ], +}; diff --git a/webui/auth0-templates/PaymentForm.js b/ui/templates/PaymentForm.js similarity index 100% rename from webui/auth0-templates/PaymentForm.js rename to ui/templates/PaymentForm.js diff --git a/webui/auth0-templates/login.html b/ui/templates/login.html similarity index 100% rename from webui/auth0-templates/login.html rename to ui/templates/login.html diff --git a/webui/auth0-templates/page.js b/ui/templates/page.js similarity index 100% rename from webui/auth0-templates/page.js rename to ui/templates/page.js diff --git a/webui/src/app/favicon.ico b/webui/src/app/favicon.ico deleted file mode 100644 index b0ae2d4..0000000 Binary files a/webui/src/app/favicon.ico and /dev/null differ