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 = () => (
+ <>
+
+
+
+
+ >
+ );
+
+ 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 (
-
-
-
-
- -
- Get started by editing{" "}
-
- src/app/page.tsx
-
- .
-
- -
- Save and see your changes instantly.
-
-
+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.
+
+ }
+ >
+ Start Your Order Today
+
+
+
+ );
+}
\ 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