diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index 329f321..9e6a85d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -5,9 +5,13 @@ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "image": "mcr.microsoft.com/devcontainers/dotnet:1-9.0-bookworm", "features": { - "ghcr.io/devcontainers/features/node:1": {}, + "ghcr.io/devcontainers/features/node:1": { + "version": "lts", + "nodeGypDependencies": true + }, "ghcr.io/jlaundry/devcontainer-features/azure-functions-core-tools:1": {}, - "ghcr.io/devcontainers/features/azure-cli:1": {} + "ghcr.io/devcontainers/features/azure-cli:1": {}, + "ghcr.io/azure/azure-dev/azd:0": {} }, "customizations": { "vscode": { @@ -16,16 +20,22 @@ "ms-dotnettools.csdevkit", "ms-azuretools.vscode-azurefunctions", "humao.rest-client", - "ms-azuretools.vscode-azurestaticwebapps" + "ms-azuretools.vscode-azurestaticwebapps", + "dbaeumer.vscode-eslint", + "esbenp.prettier-vscode", + "ms-vscode.vscode-typescript-next", + "VisualStudioExptTeam.vscodeintellicode", + "VisualStudioExptTeam.intellicode-api-usage-examples", + "ms-azuretools.vscode-bicep" ] } - } - - // Features to add to the dev container. More info: https://containers.dev/features. - // "features": {}, + }, // Use 'forwardPorts' to make a list of ports inside the container available locally. - // "forwardPorts": [5000, 5001], + "forwardPorts": [7071, 3000], + + // Install dependencies after container creation + "postCreateCommand": "cd api && dotnet restore && cd ../client && npm install", // "portsAttributes": { // "5001": { // "protocol": "https" @@ -39,5 +49,5 @@ // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. - // "remoteUser": "root" + "remoteUser": "root" } \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..86ab1e6 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,39 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "chrome", + "request": "launch", + "name": "Debug React App (Chrome)", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}/client", + "sourceMaps": true, + "sourceMapPathOverrides": { + "webpack:///src/*": "${webRoot}/src/*" + }, + "preLaunchTask": "start-vite-dev-server" + }, + { + "type": "msedge", + "request": "launch", + "name": "Debug React App (Edge)", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}/client", + "sourceMaps": true, + "sourceMapPathOverrides": { + "webpack:///src/*": "${webRoot}/src/*" + }, + "preLaunchTask": "start-vite-dev-server" + }, + { + "type": "node-terminal", + "name": "Run Development Server", + "request": "launch", + "command": "npm run dev", + "cwd": "${workspaceFolder}/client" + } + ] +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..55003c4 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,27 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "start-vite-dev-server", + "type": "npm", + "script": "dev", + "path": "client/", + "isBackground": true, + "problemMatcher": { + "owner": "custom", + "pattern": { + "regexp": "^$" + }, + "background": { + "activeOnStart": true, + "beginsPattern": ".*VITE.*", + "endsPattern": ".*ready in.*" + } + }, + "presentation": { + "reveal": "always", + "panel": "dedicated" + } + } + ] +} diff --git a/client/package-lock.json b/client/package-lock.json index 4994dbd..5c7051f 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -9,13 +9,9 @@ "version": "0.0.0", "dependencies": { "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", + "@emotion/styled": "^11.14.1", "@mui/icons-material": "^6.4.6", - "@mui/material": "^6.4.6", - "axios": "^1.8.1", - "bootstrap": "^5.3.3", - "jquery": "^3.7.1", - "popper.js": "^1.16.1", + "@mui/material": "^6.5.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^7.2.0" @@ -29,6 +25,7 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "typescript": "^5.9.2", "vite": "^6.2.0" } }, @@ -47,23 +44,23 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "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.25.9", + "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", + "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, "license": "MIT", "engines": { @@ -71,22 +68,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.10.tgz", - "integrity": "sha512-vMqyb7XCDMPvJFFOaT9kxtiRh42GwlZEg1/uIgtZshS5a/8OaduUfCi7kynKgc3Tw/6Uo2D+db9qBttghhmxwQ==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", + "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.10", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.10", - "@babel/parser": "^7.26.10", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.10", - "@babel/types": "^7.26.10", + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-compilation-targets": "^7.27.2", + "@babel/helper-module-transforms": "^7.27.3", + "@babel/helpers": "^7.27.6", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/traverse": "^7.28.0", + "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -109,15 +106,15 @@ "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.27.0.tgz", - "integrity": "sha512-VybsKvpiN1gU1sdMZIp7FcqphVVKEwcuj02x73uvcHE0PTihx1nlBcowYWhDwjpoAXRv43+gDzyggGnn1XZhVw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", + "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.0", + "@babel/types": "^7.28.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -125,14 +122,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.0.tgz", - "integrity": "sha512-LVk7fbXml0H2xH34dFzKQ7TDZ2G4/rVTOrq9V+icbbadjbVxxeFeDsNHv2SrZeWoA+6ZiTyWYWtScEIW07EAcA==", + "version": "7.27.2", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", + "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.8", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.27.2", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -141,29 +138,38 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "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.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.27.3", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", + "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1", + "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" @@ -173,9 +179,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "license": "MIT", "engines": { @@ -183,27 +189,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "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.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "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/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -211,26 +217,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz", - "integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.2.tgz", + "integrity": "sha512-/V9771t+EgXz62aCcyofnQhGM8DQACbRhvzKFsXKC9QM+5MadF8ZmIm0crDMaz3+o0h0zXfJnd4EhbYbxsrcFw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0" + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz", - "integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", + "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "license": "MIT", "dependencies": { - "@babel/types": "^7.27.0" + "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" @@ -240,13 +246,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -256,13 +262,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -272,66 +278,54 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz", - "integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", + "integrity": "sha512-KHp2IflsnGywDjBWDkR9iEqiWSpc8GIi0lgTT3mOElT0PP1tG26P4tmFI2YvAdzgq9RGyoHZQEIEdZy6Ec5xCA==", "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.27.0", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz", - "integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==", + "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.26.2", - "@babel/parser": "^7.27.0", - "@babel/types": "^7.27.0" + "@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.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.27.0.tgz", - "integrity": "sha512-19lYZFzYVQkkHkl4Cy4WrAVcqBkgvV2YM2TU3xG6DIwO7O3ecbDPfW3yM3bjAGcqcQHi+CCtjMR3dIEHxsd6bA==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", + "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.27.0", - "@babel/parser": "^7.27.0", - "@babel/template": "^7.27.0", - "@babel/types": "^7.27.0", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.27.1", + "@babel/generator": "^7.28.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.0", + "@babel/template": "^7.27.2", + "@babel/types": "^7.28.0", + "debug": "^4.3.1" }, "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.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz", - "integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==", + "version": "7.28.2", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.2.tgz", + "integrity": "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -434,9 +428,9 @@ "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==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", @@ -484,9 +478,9 @@ "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", - "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.8.tgz", + "integrity": "sha512-urAvrUedIqEiFR3FYSLTWQgLu5tb+m0qZw0NBEasUeo6wuqatkMDaRT+1uABiGXEu5vqgPd7FGE1BhsAIy9QVA==", "cpu": [ "ppc64" ], @@ -501,9 +495,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", - "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.8.tgz", + "integrity": "sha512-RONsAvGCz5oWyePVnLdZY/HHwA++nxYWIX1atInlaW6SEkwq6XkP3+cb825EUcRs5Vss/lGh/2YxAb5xqc07Uw==", "cpu": [ "arm" ], @@ -518,9 +512,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", - "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.8.tgz", + "integrity": "sha512-OD3p7LYzWpLhZEyATcTSJ67qB5D+20vbtr6vHlHWSQYhKtzUYrETuWThmzFpZtFsBIxRvhO07+UgVA9m0i/O1w==", "cpu": [ "arm64" ], @@ -535,9 +529,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", - "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.8.tgz", + "integrity": "sha512-yJAVPklM5+4+9dTeKwHOaA+LQkmrKFX96BM0A/2zQrbS6ENCmxc4OVoBs5dPkCCak2roAD+jKCdnmOqKszPkjA==", "cpu": [ "x64" ], @@ -552,9 +546,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", - "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.8.tgz", + "integrity": "sha512-Jw0mxgIaYX6R8ODrdkLLPwBqHTtYHJSmzzd+QeytSugzQ0Vg4c5rDky5VgkoowbZQahCbsv1rT1KW72MPIkevw==", "cpu": [ "arm64" ], @@ -569,9 +563,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", - "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.8.tgz", + "integrity": "sha512-Vh2gLxxHnuoQ+GjPNvDSDRpoBCUzY4Pu0kBqMBDlK4fuWbKgGtmDIeEC081xi26PPjn+1tct+Bh8FjyLlw1Zlg==", "cpu": [ "x64" ], @@ -586,9 +580,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", - "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.8.tgz", + "integrity": "sha512-YPJ7hDQ9DnNe5vxOm6jaie9QsTwcKedPvizTVlqWG9GBSq+BuyWEDazlGaDTC5NGU4QJd666V0yqCBL2oWKPfA==", "cpu": [ "arm64" ], @@ -603,9 +597,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", - "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.8.tgz", + "integrity": "sha512-MmaEXxQRdXNFsRN/KcIimLnSJrk2r5H8v+WVafRWz5xdSVmWLoITZQXcgehI2ZE6gioE6HirAEToM/RvFBeuhw==", "cpu": [ "x64" ], @@ -620,9 +614,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", - "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.8.tgz", + "integrity": "sha512-FuzEP9BixzZohl1kLf76KEVOsxtIBFwCaLupVuk4eFVnOZfU+Wsn+x5Ryam7nILV2pkq2TqQM9EZPsOBuMC+kg==", "cpu": [ "arm" ], @@ -637,9 +631,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", - "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.8.tgz", + "integrity": "sha512-WIgg00ARWv/uYLU7lsuDK00d/hHSfES5BzdWAdAig1ioV5kaFNrtK8EqGcUBJhYqotlUByUKz5Qo6u8tt7iD/w==", "cpu": [ "arm64" ], @@ -654,9 +648,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", - "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.8.tgz", + "integrity": "sha512-A1D9YzRX1i+1AJZuFFUMP1E9fMaYY+GnSQil9Tlw05utlE86EKTUA7RjwHDkEitmLYiFsRd9HwKBPEftNdBfjg==", "cpu": [ "ia32" ], @@ -671,9 +665,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", - "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.8.tgz", + "integrity": "sha512-O7k1J/dwHkY1RMVvglFHl1HzutGEFFZ3kNiDMSOyUrB7WcoHGf96Sh+64nTRT26l3GMbCW01Ekh/ThKM5iI7hQ==", "cpu": [ "loong64" ], @@ -688,9 +682,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", - "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.8.tgz", + "integrity": "sha512-uv+dqfRazte3BzfMp8PAQXmdGHQt2oC/y2ovwpTteqrMx2lwaksiFZ/bdkXJC19ttTvNXBuWH53zy/aTj1FgGw==", "cpu": [ "mips64el" ], @@ -705,9 +699,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", - "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.8.tgz", + "integrity": "sha512-GyG0KcMi1GBavP5JgAkkstMGyMholMDybAf8wF5A70CALlDM2p/f7YFE7H92eDeH/VBtFJA5MT4nRPDGg4JuzQ==", "cpu": [ "ppc64" ], @@ -722,9 +716,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", - "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.8.tgz", + "integrity": "sha512-rAqDYFv3yzMrq7GIcen3XP7TUEG/4LK86LUPMIz6RT8A6pRIDn0sDcvjudVZBiiTcZCY9y2SgYX2lgK3AF+1eg==", "cpu": [ "riscv64" ], @@ -739,9 +733,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", - "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.8.tgz", + "integrity": "sha512-Xutvh6VjlbcHpsIIbwY8GVRbwoviWT19tFhgdA7DlenLGC/mbc3lBoVb7jxj9Z+eyGqvcnSyIltYUrkKzWqSvg==", "cpu": [ "s390x" ], @@ -756,9 +750,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", - "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.8.tgz", + "integrity": "sha512-ASFQhgY4ElXh3nDcOMTkQero4b1lgubskNlhIfJrsH5OKZXDpUAKBlNS0Kx81jwOBp+HCeZqmoJuihTv57/jvQ==", "cpu": [ "x64" ], @@ -773,9 +767,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", - "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.8.tgz", + "integrity": "sha512-d1KfruIeohqAi6SA+gENMuObDbEjn22olAR7egqnkCD9DGBG0wsEARotkLgXDu6c4ncgWTZJtN5vcgxzWRMzcw==", "cpu": [ "arm64" ], @@ -790,9 +784,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", - "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.8.tgz", + "integrity": "sha512-nVDCkrvx2ua+XQNyfrujIG38+YGyuy2Ru9kKVNyh5jAys6n+l44tTtToqHjino2My8VAY6Lw9H7RI73XFi66Cg==", "cpu": [ "x64" ], @@ -807,9 +801,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", - "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.8.tgz", + "integrity": "sha512-j8HgrDuSJFAujkivSMSfPQSAa5Fxbvk4rgNAS5i3K+r8s1X0p1uOO2Hl2xNsGFppOeHOLAVgYwDVlmxhq5h+SQ==", "cpu": [ "arm64" ], @@ -824,9 +818,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", - "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.8.tgz", + "integrity": "sha512-1h8MUAwa0VhNCDp6Af0HToI2TJFAn1uqT9Al6DJVzdIBAd21m/G0Yfc77KDM3uF3T/YaOgQq3qTJHPbTOInaIQ==", "cpu": [ "x64" ], @@ -840,10 +834,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.8.tgz", + "integrity": "sha512-r2nVa5SIK9tSWd0kJd9HCffnDHKchTGikb//9c7HX+r+wHYCpQrSgxhlY6KWV1nFo1l4KFbsMlHk+L6fekLsUg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", - "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.8.tgz", + "integrity": "sha512-zUlaP2S12YhQ2UzUfcCuMDHQFJyKABkAjvO5YSndMiIkMimPmxA+BYSBikWgsRpvyxuRnow4nS5NPnf9fpv41w==", "cpu": [ "x64" ], @@ -858,9 +869,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", - "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.8.tgz", + "integrity": "sha512-YEGFFWESlPva8hGL+zvj2z/SaK+pH0SwOM0Nc/d+rVnW7GSTFlLBGzZkuSU9kFIGIo8q9X3ucpZhu8PDN5A2sQ==", "cpu": [ "arm64" ], @@ -875,9 +886,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", - "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.8.tgz", + "integrity": "sha512-hiGgGC6KZ5LZz58OL/+qVVoZiuZlUYlYHNAmczOm7bs2oE1XriPFi5ZHHrS8ACpV5EjySrnoCKmcbQMN+ojnHg==", "cpu": [ "ia32" ], @@ -892,9 +903,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", - "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.8.tgz", + "integrity": "sha512-cn3Yr7+OaaZq1c+2pe+8yxC8E144SReCQjN6/2ynubzYjvyqZjTXfQJpAcQpsdJq3My7XADANiYGHoFC69pLQw==", "cpu": [ "x64" ], @@ -909,9 +920,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", - "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", "dev": true, "license": "MIT", "dependencies": { @@ -951,9 +962,9 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.2", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", - "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", + "version": "0.21.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.0.tgz", + "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -966,9 +977,9 @@ } }, "node_modules/@eslint/config-helpers": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", - "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.3.0.tgz", + "integrity": "sha512-ViuymvFmcJi04qdZeDc2whTHryouGcDlaxPqarTD0ZE10ISpxGUVZGZDx4w01upyIynL3iu6IXH2bS1NhclQMw==", "dev": true, "license": "Apache-2.0", "engines": { @@ -976,9 +987,9 @@ } }, "node_modules/@eslint/core": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", - "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.1.tgz", + "integrity": "sha512-bkOp+iumZCCbt1K1CmWf0R9pM5yKpDv+ZXtvSyQpudrI9kuFLp+bM2WOPXImuD/ceQuaa8f5pj93Y7zyECIGNA==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -1026,13 +1037,16 @@ } }, "node_modules/@eslint/js": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", - "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.32.0.tgz", + "integrity": "sha512-BBpRFZK3eX6uMLKz8WxFOBIFFcGFJ/g8XuwjTHCqHROSIsopI+ddn/d5Cfh36+7+e5edVS8dbSHnBNhrLEX0zg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { @@ -1046,13 +1060,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", - "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.4.tgz", + "integrity": "sha512-Ul5l+lHEcw3L5+k8POx6r74mxEYKG5kOb6Xpy2gCRW6zweT6TEhAf8vhxGgjhqrd/VO/Dirhsb+1hNpD1ue9hw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.12.0", + "@eslint/core": "^0.15.1", "levn": "^0.4.1" }, "engines": { @@ -1112,9 +1126,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", - "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -1126,17 +1140,13 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", + "version": "0.3.12", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", + "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1148,25 +1158,16 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", + "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", "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==", + "version": "0.3.29", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", + "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -1174,9 +1175,9 @@ } }, "node_modules/@mui/core-downloads-tracker": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.4.9.tgz", - "integrity": "sha512-3UvsvOjqZJcokHKSzA1lskj2XMM/G5GBgge6ykwmAij2pGGxydGxAXirQlLaeoMwTKDS6BcrLqPZyPVwzri20A==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.5.0.tgz", + "integrity": "sha512-LGb8t8i6M2ZtS3Drn3GbTI1DVhDY6FJ9crEey2lZ0aN2EMZo8IZBZj9wRf4vqbZHaWjsYgtbOnJw5V8UWbmK2Q==", "license": "MIT", "funding": { "type": "opencollective", @@ -1184,9 +1185,9 @@ } }, "node_modules/@mui/icons-material": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.4.9.tgz", - "integrity": "sha512-a8l63VIscBteJlh31R88aVgHelCcrhl3Rk0GnN8znTsGhcam9mFeo4Xlw+gLUYQP7mxVcVt3WP9XJkwXWZflnw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.5.0.tgz", + "integrity": "sha512-VPuPqXqbBPlcVSA0BmnoE4knW4/xG6Thazo8vCLWkOKusko6DtwFV6B665MMWJ9j0KFohTIf3yx2zYtYacvG1g==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0" @@ -1199,7 +1200,7 @@ "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^6.4.9", + "@mui/material": "^6.5.0", "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -1210,16 +1211,16 @@ } }, "node_modules/@mui/material": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.4.9.tgz", - "integrity": "sha512-+5dExw9xUUFujIW889gB3qrfjeNo3YjYW7aWVZ6BlBIJnKpJ0jNcYZJpBUFoXt/FUV5Wy1V+/+XzR3Is2mXX2w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.5.0.tgz", + "integrity": "sha512-yjvtXoFcrPLGtgKRxFaH6OQPtcLPhkloC0BML6rBG5UeldR0nPULR/2E2BfXdo5JNV7j7lOzrrLX2Qf/iSidow==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/core-downloads-tracker": "^6.4.9", - "@mui/system": "^6.4.9", + "@mui/core-downloads-tracker": "^6.5.0", + "@mui/system": "^6.5.0", "@mui/types": "~7.2.24", - "@mui/utils": "^6.4.8", + "@mui/utils": "^6.4.9", "@popperjs/core": "^2.11.8", "@types/react-transition-group": "^4.4.12", "clsx": "^2.1.1", @@ -1238,7 +1239,7 @@ "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material-pigment-css": "^6.4.9", + "@mui/material-pigment-css": "^6.5.0", "@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" @@ -1259,13 +1260,13 @@ } }, "node_modules/@mui/private-theming": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.8.tgz", - "integrity": "sha512-sWwQoNSn6elsPTAtSqCf+w5aaGoh7AASURNmpy+QTTD/zwJ0Jgwt0ZaaP6mXq2IcgHxYnYloM/+vJgHPMkRKTQ==", + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz", + "integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/utils": "^6.4.8", + "@mui/utils": "^6.4.9", "prop-types": "^15.8.1" }, "engines": { @@ -1286,9 +1287,9 @@ } }, "node_modules/@mui/styled-engine": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.4.9.tgz", - "integrity": "sha512-qZRWO0cT407NI4ZRjZcH+1SOu8f3JzLHqdMlg52GyEufM9pkSZFnf7xjpwnlvkixcGjco6wLlMD0VB43KRcBuA==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.5.0.tgz", + "integrity": "sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1320,16 +1321,16 @@ } }, "node_modules/@mui/system": { - "version": "6.4.9", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.4.9.tgz", - "integrity": "sha512-JOj7efXGtZn+NIzX8KDyMpO1QKc0DhilPBsxvci1xAvI1e5AtAtfzrEuV5ZvN+lz2BDuzngCWlllnqQ/cg40RQ==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.5.0.tgz", + "integrity": "sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", - "@mui/private-theming": "^6.4.8", - "@mui/styled-engine": "^6.4.9", + "@mui/private-theming": "^6.4.9", + "@mui/styled-engine": "^6.5.0", "@mui/types": "~7.2.24", - "@mui/utils": "^6.4.8", + "@mui/utils": "^6.4.9", "clsx": "^2.1.1", "csstype": "^3.1.3", "prop-types": "^15.8.1" @@ -1374,9 +1375,9 @@ } }, "node_modules/@mui/utils": { - "version": "6.4.8", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.8.tgz", - "integrity": "sha512-C86gfiZ5BfZ51KqzqoHi1WuuM2QdSKoFhbkZeAfQRB+jCc4YNhhj11UXFVMMsqBgZ+Zy8IHNJW3M9Wj/LOwRXQ==", + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz", + "integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==", "license": "MIT", "dependencies": { "@babel/runtime": "^7.26.0", @@ -1413,10 +1414,17 @@ "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.37.0.tgz", - "integrity": "sha512-l7StVw6WAa8l3vA1ov80jyetOAEo1FtHvZDbzXDO/02Sq/QVvqlHkYoFwDJPIMj0GKiistsBudfx5tGFnwYWDQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.46.2.tgz", + "integrity": "sha512-Zj3Hl6sN34xJtMv7Anwb5Gu01yujyE/cLBDB2gnHTAHaWS1Z38L7kuSG+oAh0giZMqG060f/YBStXtMH6FvPMA==", "cpu": [ "arm" ], @@ -1428,9 +1436,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.37.0.tgz", - "integrity": "sha512-6U3SlVyMxezt8Y+/iEBcbp945uZjJwjZimu76xoG7tO1av9VO691z8PkhzQ85ith2I8R2RddEPeSfcbyPfD4hA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.46.2.tgz", + "integrity": "sha512-nTeCWY83kN64oQ5MGz3CgtPx8NSOhC5lWtsjTs+8JAJNLcP3QbLCtDDgUKQc/Ro/frpMq4SHUaHN6AMltcEoLQ==", "cpu": [ "arm64" ], @@ -1442,9 +1450,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.37.0.tgz", - "integrity": "sha512-+iTQ5YHuGmPt10NTzEyMPbayiNTcOZDWsbxZYR1ZnmLnZxG17ivrPSWFO9j6GalY0+gV3Jtwrrs12DBscxnlYA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.46.2.tgz", + "integrity": "sha512-HV7bW2Fb/F5KPdM/9bApunQh68YVDU8sO8BvcW9OngQVN3HHHkw99wFupuUJfGR9pYLLAjcAOA6iO+evsbBaPQ==", "cpu": [ "arm64" ], @@ -1456,9 +1464,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.37.0.tgz", - "integrity": "sha512-m8W2UbxLDcmRKVjgl5J/k4B8d7qX2EcJve3Sut7YGrQoPtCIQGPH5AMzuFvYRWZi0FVS0zEY4c8uttPfX6bwYQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.46.2.tgz", + "integrity": "sha512-SSj8TlYV5nJixSsm/y3QXfhspSiLYP11zpfwp6G/YDXctf3Xkdnk4woJIF5VQe0of2OjzTt8EsxnJDCdHd2xMA==", "cpu": [ "x64" ], @@ -1470,9 +1478,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.37.0.tgz", - "integrity": "sha512-FOMXGmH15OmtQWEt174v9P1JqqhlgYge/bUjIbiVD1nI1NeJ30HYT9SJlZMqdo1uQFyt9cz748F1BHghWaDnVA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.46.2.tgz", + "integrity": "sha512-ZyrsG4TIT9xnOlLsSSi9w/X29tCbK1yegE49RYm3tu3wF1L/B6LVMqnEWyDB26d9Ecx9zrmXCiPmIabVuLmNSg==", "cpu": [ "arm64" ], @@ -1484,9 +1492,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.37.0.tgz", - "integrity": "sha512-SZMxNttjPKvV14Hjck5t70xS3l63sbVwl98g3FlVVx2YIDmfUIy29jQrsw06ewEYQ8lQSuY9mpAPlmgRD2iSsA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.46.2.tgz", + "integrity": "sha512-pCgHFoOECwVCJ5GFq8+gR8SBKnMO+xe5UEqbemxBpCKYQddRQMgomv1104RnLSg7nNvgKy05sLsY51+OVRyiVw==", "cpu": [ "x64" ], @@ -1498,9 +1506,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.37.0.tgz", - "integrity": "sha512-hhAALKJPidCwZcj+g+iN+38SIOkhK2a9bqtJR+EtyxrKKSt1ynCBeqrQy31z0oWU6thRZzdx53hVgEbRkuI19w==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.46.2.tgz", + "integrity": "sha512-EtP8aquZ0xQg0ETFcxUbU71MZlHaw9MChwrQzatiE8U/bvi5uv/oChExXC4mWhjiqK7azGJBqU0tt5H123SzVA==", "cpu": [ "arm" ], @@ -1512,9 +1520,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.37.0.tgz", - "integrity": "sha512-jUb/kmn/Gd8epbHKEqkRAxq5c2EwRt0DqhSGWjPFxLeFvldFdHQs/n8lQ9x85oAeVb6bHcS8irhTJX2FCOd8Ag==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.46.2.tgz", + "integrity": "sha512-qO7F7U3u1nfxYRPM8HqFtLd+raev2K137dsV08q/LRKRLEc7RsiDWihUnrINdsWQxPR9jqZ8DIIZ1zJJAm5PjQ==", "cpu": [ "arm" ], @@ -1526,9 +1534,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.37.0.tgz", - "integrity": "sha512-oNrJxcQT9IcbcmKlkF+Yz2tmOxZgG9D9GRq+1OE6XCQwCVwxixYAa38Z8qqPzQvzt1FCfmrHX03E0pWoXm1DqA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.46.2.tgz", + "integrity": "sha512-3dRaqLfcOXYsfvw5xMrxAk9Lb1f395gkoBYzSFcc/scgRFptRXL9DOaDpMiehf9CO8ZDRJW2z45b6fpU5nwjng==", "cpu": [ "arm64" ], @@ -1540,9 +1548,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.37.0.tgz", - "integrity": "sha512-pfxLBMls+28Ey2enpX3JvjEjaJMBX5XlPCZNGxj4kdJyHduPBXtxYeb8alo0a7bqOoWZW2uKynhHxF/MWoHaGQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.46.2.tgz", + "integrity": "sha512-fhHFTutA7SM+IrR6lIfiHskxmpmPTJUXpWIsBXpeEwNgZzZZSg/q4i6FU4J8qOGyJ0TR+wXBwx/L7Ho9z0+uDg==", "cpu": [ "arm64" ], @@ -1554,9 +1562,9 @@ ] }, "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.37.0.tgz", - "integrity": "sha512-yCE0NnutTC/7IGUq/PUHmoeZbIwq3KRh02e9SfFh7Vmc1Z7atuJRYWhRME5fKgT8aS20mwi1RyChA23qSyRGpA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.46.2.tgz", + "integrity": "sha512-i7wfGFXu8x4+FRqPymzjD+Hyav8l95UIZ773j7J7zRYc3Xsxy2wIn4x+llpunexXe6laaO72iEjeeGyUFmjKeA==", "cpu": [ "loong64" ], @@ -1567,10 +1575,10 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.37.0.tgz", - "integrity": "sha512-NxcICptHk06E2Lh3a4Pu+2PEdZ6ahNHuK7o6Np9zcWkrBMuv21j10SQDJW3C9Yf/A/P7cutWoC/DptNLVsZ0VQ==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.46.2.tgz", + "integrity": "sha512-B/l0dFcHVUnqcGZWKcWBSV2PF01YUt0Rvlurci5P+neqY/yMKchGU8ullZvIv5e8Y1C6wOn+U03mrDylP5q9Yw==", "cpu": [ "ppc64" ], @@ -1582,9 +1590,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.37.0.tgz", - "integrity": "sha512-PpWwHMPCVpFZLTfLq7EWJWvrmEuLdGn1GMYcm5MV7PaRgwCEYJAwiN94uBuZev0/J/hFIIJCsYw4nLmXA9J7Pw==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.46.2.tgz", + "integrity": "sha512-32k4ENb5ygtkMwPMucAb8MtV8olkPT03oiTxJbgkJa7lJ7dZMr0GCFJlyvy+K8iq7F/iuOr41ZdUHaOiqyR3iQ==", "cpu": [ "riscv64" ], @@ -1596,9 +1604,9 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.37.0.tgz", - "integrity": "sha512-DTNwl6a3CfhGTAOYZ4KtYbdS8b+275LSLqJVJIrPa5/JuIufWWZ/QFvkxp52gpmguN95eujrM68ZG+zVxa8zHA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.46.2.tgz", + "integrity": "sha512-t5B2loThlFEauloaQkZg9gxV05BYeITLvLkWOkRXogP4qHXLkWSbSHKM9S6H1schf/0YGP/qNKtiISlxvfmmZw==", "cpu": [ "riscv64" ], @@ -1610,9 +1618,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.37.0.tgz", - "integrity": "sha512-hZDDU5fgWvDdHFuExN1gBOhCuzo/8TMpidfOR+1cPZJflcEzXdCy1LjnklQdW8/Et9sryOPJAKAQRw8Jq7Tg+A==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.46.2.tgz", + "integrity": "sha512-YKjekwTEKgbB7n17gmODSmJVUIvj8CX7q5442/CK80L8nqOUbMtf8b01QkG3jOqyr1rotrAnW6B/qiHwfcuWQA==", "cpu": [ "s390x" ], @@ -1624,9 +1632,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.37.0.tgz", - "integrity": "sha512-pKivGpgJM5g8dwj0ywBwe/HeVAUSuVVJhUTa/URXjxvoyTT/AxsLTAbkHkDHG7qQxLoW2s3apEIl26uUe08LVQ==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.46.2.tgz", + "integrity": "sha512-Jj5a9RUoe5ra+MEyERkDKLwTXVu6s3aACP51nkfnK9wJTraCC8IMe3snOfALkrjTYd2G1ViE1hICj0fZ7ALBPA==", "cpu": [ "x64" ], @@ -1638,9 +1646,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.37.0.tgz", - "integrity": "sha512-E2lPrLKE8sQbY/2bEkVTGDEk4/49UYRVWgj90MY8yPjpnGBQ+Xi1Qnr7b7UIWw1NOggdFQFOLZ8+5CzCiz143w==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.46.2.tgz", + "integrity": "sha512-7kX69DIrBeD7yNp4A5b81izs8BqoZkCIaxQaOpumcJ1S/kmqNFjPhDu1LHeVXv0SexfHQv5cqHsxLOjETuqDuA==", "cpu": [ "x64" ], @@ -1652,9 +1660,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.37.0.tgz", - "integrity": "sha512-Jm7biMazjNzTU4PrQtr7VS8ibeys9Pn29/1bm4ph7CP2kf21950LgN+BaE2mJ1QujnvOc6p54eWWiVvn05SOBg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.46.2.tgz", + "integrity": "sha512-wiJWMIpeaak/jsbaq2HMh/rzZxHVW1rU6coyeNNpMwk5isiPjSTx0a4YLSlYDwBH/WBvLz+EtsNqQScZTLJy3g==", "cpu": [ "arm64" ], @@ -1666,9 +1674,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.37.0.tgz", - "integrity": "sha512-e3/1SFm1OjefWICB2Ucstg2dxYDkDTZGDYgwufcbsxTHyqQps1UQf33dFEChBNmeSsTOyrjw2JJq0zbG5GF6RA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.46.2.tgz", + "integrity": "sha512-gBgaUDESVzMgWZhcyjfs9QFK16D8K6QZpwAaVNJxYDLHWayOta4ZMjGm/vsAEy3hvlS2GosVFlBlP9/Wb85DqQ==", "cpu": [ "ia32" ], @@ -1680,9 +1688,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.37.0.tgz", - "integrity": "sha512-LWbXUBwn/bcLx2sSsqy7pK5o+Nr+VCoRoAohfJ5C/aBio9nfJmGQqHAhU6pwxV/RmyTk5AqdySma7uwWGlmeuA==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.46.2.tgz", + "integrity": "sha512-CvUo2ixeIQGtF6WvuB87XWqPQkoFAFqW+HUo/WzHwuHDvIwZCtjdWXoYCcr06iKGydiqTclC4jU/TNObC/xKZg==", "cpu": [ "x64" ], @@ -1708,9 +1716,9 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.8", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.8.tgz", - "integrity": "sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "license": "MIT", "dependencies": { @@ -1738,16 +1746,10 @@ "@babel/types": "^7.20.7" } }, - "node_modules/@types/cookie": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz", - "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==", - "license": "MIT" - }, "node_modules/@types/estree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", - "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, @@ -1765,24 +1767,24 @@ "license": "MIT" }, "node_modules/@types/prop-types": { - "version": "15.7.14", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", - "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "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.0.12", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.12.tgz", - "integrity": "sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==", + "version": "19.1.9", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.9.tgz", + "integrity": "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA==", "license": "MIT", "dependencies": { "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "19.0.4", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.0.4.tgz", - "integrity": "sha512-4fSQ8vWFkg+TGhePfUzVmat3eC14TXYSsiiDSLI0dVLsrm9gZFABjPy/Qu6TKgl1tq1Bu1yDsuQgY3A3DOjCcg==", + "version": "19.1.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.7.tgz", + "integrity": "sha512-i5ZzwYpqjmrKenzkoLM2Ibzt6mAsM7pxB6BCIouEVVmgiqaMj1TjaK7hnA36hbW5aZv20kx7Lw6hWzPWg0Rurw==", "dev": true, "license": "MIT", "peerDependencies": { @@ -1799,29 +1801,30 @@ } }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", "bin": { @@ -1881,23 +1884,6 @@ "dev": true, "license": "Python-2.0" }, - "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/axios": { - "version": "1.8.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", - "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.0", - "proxy-from-env": "^1.1.0" - } - }, "node_modules/babel-plugin-macros": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", @@ -1920,29 +1906,10 @@ "dev": true, "license": "MIT" }, - "node_modules/bootstrap": { - "version": "5.3.3", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.3.3.tgz", - "integrity": "sha512-8HLCdWgyoMguSO9o+aH+iuZ+aht+mzW0u3HIMzVu7Srrpv7EBBxTnrFlSCskwdY1+EOFQSm7uMJhNQHkdPcmjg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/twbs" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/bootstrap" - } - ], - "license": "MIT", - "peerDependencies": { - "@popperjs/core": "^2.11.8" - } - }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { @@ -1951,9 +1918,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.25.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", + "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { @@ -1971,10 +1938,10 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", + "caniuse-lite": "^1.0.30001726", + "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" @@ -1983,19 +1950,6 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, - "node_modules/call-bind-apply-helpers": { - "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==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2006,9 +1960,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001707", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", - "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", + "version": "1.0.30001731", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001731.tgz", + "integrity": "sha512-lDdp2/wrOmTRWuoB5DpfNkC0rJDU8DqRa6nYL6HK6sytw70QMopt/NIc/9SM7ylItlBWfACXk0tEn37UWM/+mg==", "dev": true, "funding": [ { @@ -2072,18 +2026,6 @@ "dev": true, "license": "MIT" }, - "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", @@ -2153,9 +2095,9 @@ "license": "MIT" }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2176,15 +2118,6 @@ "dev": true, "license": "MIT" }, - "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/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -2195,24 +2128,10 @@ "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==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/electron-to-chromium": { - "version": "1.5.128", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.128.tgz", - "integrity": "sha512-bo1A4HH/NS522Ws0QNFIzyPcyUUNV/yyy70Ho1xqfGYzPUme2F/xr4tlEOuM6/A538U1vDA7a4XfCd1CKRegKQ==", + "version": "1.5.194", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.194.tgz", + "integrity": "sha512-SdnWJwSUot04UR51I2oPD8kuP2VI37/CADR1OHsFOUzZIvfWJBO6q11k5P/uKNyTT3cdOsnyjkrZ+DDShqYqJA==", "dev": true, "license": "ISC" }, @@ -2225,55 +2144,10 @@ "is-arrayish": "^0.2.1" } }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "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==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "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==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/esbuild": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", - "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "version": "0.25.8", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.8.tgz", + "integrity": "sha512-vVC0USHGtMi8+R4Kz8rt6JhEWLxsv9Rnu/lGYbPR8u47B+DCBksq9JarW0zOO7bs37hyOK1l2/oqtbciutL5+Q==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2284,31 +2158,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.1", - "@esbuild/android-arm": "0.25.1", - "@esbuild/android-arm64": "0.25.1", - "@esbuild/android-x64": "0.25.1", - "@esbuild/darwin-arm64": "0.25.1", - "@esbuild/darwin-x64": "0.25.1", - "@esbuild/freebsd-arm64": "0.25.1", - "@esbuild/freebsd-x64": "0.25.1", - "@esbuild/linux-arm": "0.25.1", - "@esbuild/linux-arm64": "0.25.1", - "@esbuild/linux-ia32": "0.25.1", - "@esbuild/linux-loong64": "0.25.1", - "@esbuild/linux-mips64el": "0.25.1", - "@esbuild/linux-ppc64": "0.25.1", - "@esbuild/linux-riscv64": "0.25.1", - "@esbuild/linux-s390x": "0.25.1", - "@esbuild/linux-x64": "0.25.1", - "@esbuild/netbsd-arm64": "0.25.1", - "@esbuild/netbsd-x64": "0.25.1", - "@esbuild/openbsd-arm64": "0.25.1", - "@esbuild/openbsd-x64": "0.25.1", - "@esbuild/sunos-x64": "0.25.1", - "@esbuild/win32-arm64": "0.25.1", - "@esbuild/win32-ia32": "0.25.1", - "@esbuild/win32-x64": "0.25.1" + "@esbuild/aix-ppc64": "0.25.8", + "@esbuild/android-arm": "0.25.8", + "@esbuild/android-arm64": "0.25.8", + "@esbuild/android-x64": "0.25.8", + "@esbuild/darwin-arm64": "0.25.8", + "@esbuild/darwin-x64": "0.25.8", + "@esbuild/freebsd-arm64": "0.25.8", + "@esbuild/freebsd-x64": "0.25.8", + "@esbuild/linux-arm": "0.25.8", + "@esbuild/linux-arm64": "0.25.8", + "@esbuild/linux-ia32": "0.25.8", + "@esbuild/linux-loong64": "0.25.8", + "@esbuild/linux-mips64el": "0.25.8", + "@esbuild/linux-ppc64": "0.25.8", + "@esbuild/linux-riscv64": "0.25.8", + "@esbuild/linux-s390x": "0.25.8", + "@esbuild/linux-x64": "0.25.8", + "@esbuild/netbsd-arm64": "0.25.8", + "@esbuild/netbsd-x64": "0.25.8", + "@esbuild/openbsd-arm64": "0.25.8", + "@esbuild/openbsd-x64": "0.25.8", + "@esbuild/openharmony-arm64": "0.25.8", + "@esbuild/sunos-x64": "0.25.8", + "@esbuild/win32-arm64": "0.25.8", + "@esbuild/win32-ia32": "0.25.8", + "@esbuild/win32-x64": "0.25.8" } }, "node_modules/escalade": { @@ -2334,20 +2209,20 @@ } }, "node_modules/eslint": { - "version": "9.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", - "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", + "version": "9.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", + "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.2", - "@eslint/config-helpers": "^0.2.0", - "@eslint/core": "^0.12.0", + "@eslint/config-array": "^0.21.0", + "@eslint/config-helpers": "^0.3.0", + "@eslint/core": "^0.15.0", "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.23.0", - "@eslint/plugin-kit": "^0.2.7", + "@eslint/js": "9.32.0", + "@eslint/plugin-kit": "^0.3.4", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", @@ -2358,9 +2233,9 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -2408,9 +2283,9 @@ } }, "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.19", - "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", - "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "version": "0.4.20", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.20.tgz", + "integrity": "sha512-XpbHQ2q5gUF8BGOX4dHe+71qoirYMhApEPZ7sfhF/dNnOF1UXnCMGZf79SFTBO7Bz5YEIT4TMieSlJBWhP9WBA==", "dev": true, "license": "MIT", "peerDependencies": { @@ -2418,9 +2293,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2435,9 +2310,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -2448,15 +2323,15 @@ } }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2532,6 +2407,21 @@ "dev": true, "license": "MIT" }, + "node_modules/fdir": { + "version": "6.4.6", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.6.tgz", + "integrity": "sha512-hiFoqpyZcfNm1yc4u8oWCf9A2c4D3QjCrks3zmoVKVxpQRzmPNar1hUJcBG2RQHvEVGDN+Jm81ZheVLAQMK6+w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -2589,41 +2479,6 @@ "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/form-data": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", - "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2658,43 +2513,6 @@ "node": ">=6.9.0" } }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2721,18 +2539,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -2743,33 +2549,6 @@ "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -2884,12 +2663,6 @@ "dev": true, "license": "ISC" }, - "node_modules/jquery": { - "version": "3.7.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.7.1.tgz", - "integrity": "sha512-m4avr8yL8kmFN8psrbFFFmB/If14iN5o9nw/NgnnM+kybDJpRsAynV2BsfpTYrTRysYUdADVD7CkUUizgkpLfg==", - "license": "MIT" - }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -3036,36 +2809,6 @@ "yallist": "^3.0.2" } }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "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", @@ -3248,21 +2991,23 @@ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "license": "ISC" }, - "node_modules/popper.js": { - "version": "1.16.1", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz", - "integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ==", - "deprecated": "You can find the new Popper v2 at @popperjs/core, this package is dedicated to the legacy v1", + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, "license": "MIT", + "engines": { + "node": ">=12" + }, "funding": { - "type": "opencollective", - "url": "https://opencollective.com/popperjs" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -3280,7 +3025,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -3315,12 +3060,6 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, - "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", @@ -3332,36 +3071,36 @@ } }, "node_modules/react": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", - "integrity": "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.1.tgz", + "integrity": "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.0.0.tgz", - "integrity": "sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-Dlq/5LAZgF0Gaz6yiqZCf6VCcZs1ghAJyrsu84Q/GT0gV+mCxbfmKNoGRKBYMJ8IEdGPqu49YWXD02GCknEDkw==", "license": "MIT", "dependencies": { - "scheduler": "^0.25.0" + "scheduler": "^0.26.0" }, "peerDependencies": { - "react": "^19.0.0" + "react": "^19.1.1" } }, "node_modules/react-is": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.0.0.tgz", - "integrity": "sha512-H91OHcwjZsbq3ClIDHMzBShc1rotbfACdWENsmEf0IFvZ3FgGPtdHMcsv45bQ1hAbgdfiA8SnxTKfDS+x/8m2g==", + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.1.tgz", + "integrity": "sha512-tr41fA15Vn8p4X9ntI+yCyeGSf1TlYaY5vlTZfQmeLBrFo3psOPX6HhTDnFNL9uj3EhP0KAQ80cugCl4b4BERA==", "license": "MIT" }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", "engines": { @@ -3369,15 +3108,13 @@ } }, "node_modules/react-router": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.4.0.tgz", - "integrity": "sha512-Y2g5ObjkvX3VFeVt+0CIPuYd9PpgqCslG7ASSIdN73LwA1nNWzcMLaoMRJfP3prZFI92svxFwbn7XkLJ+UPQ6A==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.7.1.tgz", + "integrity": "sha512-jVKHXoWRIsD/qS6lvGveckwb862EekvapdHJN/cGmzw40KnJH5gg53ujOJ4qX6EKIK9LSBfFed/xiQ5yeXNrUA==", "license": "MIT", "dependencies": { - "@types/cookie": "^0.6.0", "cookie": "^1.0.1", - "set-cookie-parser": "^2.6.0", - "turbo-stream": "2.4.0" + "set-cookie-parser": "^2.6.0" }, "engines": { "node": ">=20.0.0" @@ -3393,12 +3130,12 @@ } }, "node_modules/react-router-dom": { - "version": "7.4.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.4.0.tgz", - "integrity": "sha512-VlksBPf3n2bijPvnA7nkTsXxMAKOj+bWp4R9c3i+bnwlSOFAGOkJkKhzy/OsRkWaBMICqcAl1JDzh9ZSOze9CA==", + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.7.1.tgz", + "integrity": "sha512-bavdk2BA5r3MYalGKZ01u8PGuDBloQmzpBZVhDLrOOv1N943Wq6dcM9GhB3x8b7AbqPMEezauv4PeGkAJfy7FQ==", "license": "MIT", "dependencies": { - "react-router": "7.4.0" + "react-router": "7.7.1" }, "engines": { "node": ">=20.0.0" @@ -3424,12 +3161,6 @@ "react-dom": ">=16.6.0" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.1", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", - "license": "MIT" - }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", @@ -3460,13 +3191,13 @@ } }, "node_modules/rollup": { - "version": "4.37.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.37.0.tgz", - "integrity": "sha512-iAtQy/L4QFU+rTJ1YUjXqJOJzuwEghqWzCEYD2FEghT7Gsy1VdABntrO4CLopA5IkflTyqNiLNwPcOJ3S7UKLg==", + "version": "4.46.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.46.2.tgz", + "integrity": "sha512-WMmLFI+Boh6xbop+OAGo9cQ3OgX9MIg7xOQjn+pTCwOkk+FNDAeAemXkJ3HzDJrVXleLOFVa1ipuc1AmEx1Dwg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -3476,40 +3207,33 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.37.0", - "@rollup/rollup-android-arm64": "4.37.0", - "@rollup/rollup-darwin-arm64": "4.37.0", - "@rollup/rollup-darwin-x64": "4.37.0", - "@rollup/rollup-freebsd-arm64": "4.37.0", - "@rollup/rollup-freebsd-x64": "4.37.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.37.0", - "@rollup/rollup-linux-arm-musleabihf": "4.37.0", - "@rollup/rollup-linux-arm64-gnu": "4.37.0", - "@rollup/rollup-linux-arm64-musl": "4.37.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.37.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.37.0", - "@rollup/rollup-linux-riscv64-gnu": "4.37.0", - "@rollup/rollup-linux-riscv64-musl": "4.37.0", - "@rollup/rollup-linux-s390x-gnu": "4.37.0", - "@rollup/rollup-linux-x64-gnu": "4.37.0", - "@rollup/rollup-linux-x64-musl": "4.37.0", - "@rollup/rollup-win32-arm64-msvc": "4.37.0", - "@rollup/rollup-win32-ia32-msvc": "4.37.0", - "@rollup/rollup-win32-x64-msvc": "4.37.0", + "@rollup/rollup-android-arm-eabi": "4.46.2", + "@rollup/rollup-android-arm64": "4.46.2", + "@rollup/rollup-darwin-arm64": "4.46.2", + "@rollup/rollup-darwin-x64": "4.46.2", + "@rollup/rollup-freebsd-arm64": "4.46.2", + "@rollup/rollup-freebsd-x64": "4.46.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.46.2", + "@rollup/rollup-linux-arm-musleabihf": "4.46.2", + "@rollup/rollup-linux-arm64-gnu": "4.46.2", + "@rollup/rollup-linux-arm64-musl": "4.46.2", + "@rollup/rollup-linux-loongarch64-gnu": "4.46.2", + "@rollup/rollup-linux-ppc64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-gnu": "4.46.2", + "@rollup/rollup-linux-riscv64-musl": "4.46.2", + "@rollup/rollup-linux-s390x-gnu": "4.46.2", + "@rollup/rollup-linux-x64-gnu": "4.46.2", + "@rollup/rollup-linux-x64-musl": "4.46.2", + "@rollup/rollup-win32-arm64-msvc": "4.46.2", + "@rollup/rollup-win32-ia32-msvc": "4.46.2", + "@rollup/rollup-win32-x64-msvc": "4.46.2", "fsevents": "~2.3.2" } }, - "node_modules/rollup/node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true, - "license": "MIT" - }, "node_modules/scheduler": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", - "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", "license": "MIT" }, "node_modules/semver": { @@ -3614,11 +3338,22 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/turbo-stream": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/turbo-stream/-/turbo-stream-2.4.0.tgz", - "integrity": "sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==", - "license": "ISC" + "node_modules/tinyglobby": { + "version": "0.2.14", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.14.tgz", + "integrity": "sha512-tX5e7OM1HnYr2+a2C/4V0htOcSQcoSTH9KgJnVvNm5zm/cyEWKJ7j7YutsH9CxMdtOkkLFy2AHrMci9IM8IPZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.4.4", + "picomatch": "^4.0.2" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } }, "node_modules/type-check": { "version": "0.4.0", @@ -3633,6 +3368,20 @@ "node": ">= 0.8.0" } }, + "node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -3675,15 +3424,18 @@ } }, "node_modules/vite": { - "version": "6.2.3", - "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.3.tgz", - "integrity": "sha512-IzwM54g4y9JA/xAeBPNaDXiBF8Jsgl3VBQ2YQ/wOY6fyW3xMdSoltIV3Bo59DErdqdE6RxUfv8W69DvUorE4Eg==", + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", + "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "dev": true, "license": "MIT", "dependencies": { "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", "postcss": "^8.5.3", - "rollup": "^4.30.1" + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" }, "bin": { "vite": "bin/vite.js" @@ -3780,9 +3532,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", - "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.0.tgz", + "integrity": "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ==", "dev": true, "license": "ISC", "optional": true, @@ -3791,7 +3543,7 @@ "yaml": "bin.mjs" }, "engines": { - "node": ">= 14" + "node": ">= 14.6" } }, "node_modules/yocto-queue": { diff --git a/client/package.json b/client/package.json index 3670ec2..a605b71 100644 --- a/client/package.json +++ b/client/package.json @@ -4,20 +4,19 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --host", - "build": "vite build", + "dev": "vite --host --port 3000", + "build": "tsc && vite build", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "clean:vite": "rm -rf node_modules/.vite && rm -rf build", + "type-check": "tsc --noEmit", + "dev:build": "tsc && vite build && vite --host --port 3000" }, "dependencies": { "@emotion/react": "^11.14.0", - "@emotion/styled": "^11.14.0", + "@emotion/styled": "^11.14.1", "@mui/icons-material": "^6.4.6", - "@mui/material": "^6.4.6", - "axios": "^1.8.1", - "bootstrap": "^5.3.3", - "jquery": "^3.7.1", - "popper.js": "^1.16.1", + "@mui/material": "^6.5.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-router-dom": "^7.2.0" @@ -31,6 +30,7 @@ "eslint-plugin-react-hooks": "^5.1.0", "eslint-plugin-react-refresh": "^0.4.19", "globals": "^15.15.0", + "typescript": "^5.9.2", "vite": "^6.2.0" } } diff --git a/client/sample.env b/client/sample.env new file mode 100644 index 0000000..7cd2fd8 --- /dev/null +++ b/client/sample.env @@ -0,0 +1 @@ +VITE_REACT_APP_BACKEND_URL=http://localhost:7071 diff --git a/client/src/App/App.css b/client/src/App/App.css deleted file mode 100644 index 45acf08..0000000 --- a/client/src/App/App.css +++ /dev/null @@ -1,17 +0,0 @@ -/* Application styles */ -.app { - background-color: #fff; - padding: 0 !important; - min-width: 375px; -} - -.main--home { - min-height: 40em; - width: 50%; -} -.main--details { - min-height: 40em; -} -.debug { - border: 1px solid red; -} \ No newline at end of file diff --git a/client/src/App/App.jsx b/client/src/App/App.jsx deleted file mode 100644 index 3aa4281..0000000 --- a/client/src/App/App.jsx +++ /dev/null @@ -1,61 +0,0 @@ -import React, {useState, useEffect} from 'react'; -import { BrowserRouter, Route, Routes } from 'react-router-dom'; - -// Context for user authentication -import { AuthContext } from '../contexts/AuthContext'; - -// App shell components -import AppHeader from '../components/AppHeader/AppHeader'; -import AppFooter from '../components/AppFooter/AppFooter'; - -// React Router page components -import Home from '../pages/Home/Home'; -import Search from '../pages/Search/Search'; -import Details from '../pages/Details/Details'; - -// Bootstrap styles, optionally with jQuery and Popper -import 'bootstrap/dist/css/bootstrap.min.css'; - -// Custom app styles -import './App.css'; - -export default function App() { - // React Hook: useState with a var name, set function, & default value - const [user, setUser] = useState({}); - - // Fetch authentication API & set user state - async function fetchAuth() { - const response = await fetch("/.auth/me"); - if (response) { - const contentType = response.headers.get("content-type"); - if (contentType && contentType.indexOf("application/json") !== -1) { - response.json() - .then(response => setUser(response)) - .catch(error => console.error('Error:', error)); - } - } - } - - // React Hook: useEffect when component changes - // Empty array ensure this only runs once on mount - useEffect(() => { - fetchAuth() - }, []); - - return ( - -
- - - - } /> - } /> - }/> - } /> - - - {} -
-
- ); -} diff --git a/client/src/App/App.tsx b/client/src/App/App.tsx new file mode 100644 index 0000000..826c5ad --- /dev/null +++ b/client/src/App/App.tsx @@ -0,0 +1,82 @@ +import React, {useState, useEffect, lazy, Suspense} from 'react'; +import { BrowserRouter, Route, Routes } from 'react-router-dom'; + +// App shell components +import AppHeader from '../components/AppHeader'; +import AppFooter from '../components/AppFooter'; + +// React Router page components - Lazy load routes +const Home = lazy(() => import('../pages/Home/Home')); +const Search = lazy(() => import('../pages/Search/Search')); +const Details = lazy(() => import('../pages/Details/Details')); + +// Styled components +import { AppContainer } from './styled'; + +// Types +interface AuthUser { + clientPrincipal?: { + userId: string; + userRoles: string[]; + claims: Array<{typ: string, val: string}>; + identityProvider: string; + userDetails: string; + }; +} + +export default function App(): React.ReactElement { + // React Hook: useState with a var name, set function, & default value + const [user, setUser] = useState({}); + + // Fetch authentication API & set user state + async function fetchAuth(): Promise { + try { + const response = await fetch("/.auth/me"); + if (response.ok) { + const contentType = response.headers.get("content-type"); + if (contentType && contentType.indexOf("application/json") !== -1) { + const authData: AuthUser = await response.json(); + setUser(authData); + } + } + } catch (error) { + console.error('Authentication error:', error); + } + } + + // React Hook: useEffect when component changes + // Empty array ensure this only runs once on mount + useEffect(() => { + fetchAuth() + }, []); + + // Simple loading component + const LoadingFallback = () => ( +
+ Loading... +
+ ); + + return ( + + + + }> + + } /> + } /> + }/> + } /> + + + + {} + + ); +} diff --git a/client/src/App/styled.tsx b/client/src/App/styled.tsx new file mode 100644 index 0000000..fac2d72 --- /dev/null +++ b/client/src/App/styled.tsx @@ -0,0 +1,14 @@ +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; + +// Styled components for App +export const AppContainer = styled(Box)(() => ({ + backgroundColor: '#fff', + padding: '0 !important', + minWidth: '375px', +})); + +export const HomeMain = styled(Box)(() => ({ + minHeight: '40em', + width: '50%', +})); diff --git a/client/src/components/AppFooter.tsx b/client/src/components/AppFooter.tsx new file mode 100644 index 0000000..9928570 --- /dev/null +++ b/client/src/components/AppFooter.tsx @@ -0,0 +1,33 @@ +import Typography from '@mui/material/Typography'; +import Container from '@mui/material/Container'; +import Box from '@mui/material/Box'; + +export default function AppFooter() { + const currentYear = new Date().getFullYear(); + + return ( + + {/* Top border separator */} + + + {/* Spacer */} + + + {/* Footer content */} + + + + © {currentYear} Microsoft + + + + + ); +}; diff --git a/client/src/components/AppFooter/AppFooter.css b/client/src/components/AppFooter/AppFooter.css deleted file mode 100644 index 9466fc7..0000000 --- a/client/src/components/AppFooter/AppFooter.css +++ /dev/null @@ -1,7 +0,0 @@ -.footer { - margin-top: 1em; - padding: 1em; - font-size: 0.85em; - color: #666666; - text-align: center; -} diff --git a/client/src/components/AppFooter/AppFooter.jsx b/client/src/components/AppFooter/AppFooter.jsx deleted file mode 100644 index 03c2b3f..0000000 --- a/client/src/components/AppFooter/AppFooter.jsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; - -import './AppFooter.css'; - -export default function AppFooter() { - - const currentYear = new Date().getFullYear(); - - return ( -
-
- © {currentYear} Microsoft -
- ); -}; diff --git a/client/src/components/AppHeader.tsx b/client/src/components/AppHeader.tsx new file mode 100644 index 0000000..cda0242 --- /dev/null +++ b/client/src/components/AppHeader.tsx @@ -0,0 +1,106 @@ +import React from 'react'; +import AppBar from '@mui/material/AppBar'; +import Toolbar from '@mui/material/Toolbar'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import Stack from '@mui/material/Stack'; +import IconButton from '@mui/material/IconButton'; +import MenuIcon from '@mui/icons-material/Menu'; +import Menu from '@mui/material/Menu'; +import MenuItem from '@mui/material/MenuItem'; + +import logo from '../images/microsoft_small.png'; + +export default function AppHeader() { + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleMenuOpen = (event) => { + setAnchorEl(event.currentTarget); + }; + + const handleMenuClose = () => { + setAnchorEl(null); + }; + + return ( +
+ + + {/* Logo */} + + Microsoft + + + {/* Nav links - desktop */} + + + + + + + + {/* Hamburger menu - mobile */} + + + + + + + Search + + + Learn more + + + + + {/* Auth functionality removed */} + + +
+ ); +}; diff --git a/client/src/components/AppHeader/AppHeader.css b/client/src/components/AppHeader/AppHeader.css deleted file mode 100644 index 76de423..0000000 --- a/client/src/components/AppHeader/AppHeader.css +++ /dev/null @@ -1,27 +0,0 @@ -.header { - background-color: #0078d7; - color: #eee; -} - -.nav-bar-search { - margin-left: 10px;; -} - -.header .nav-link { - color: #fff; -} -.nav-link:hover { color: #eee; } - -.auth-link { color: #fff; } -.auth-link:hover { color: #eee; } - -.navbar-brand-image { - height: 40px; /* Adjust the height as needed */ -} - -/* Ensure the logo keeps its aspect ratio using modern CSS */ -.navbar-logo { - height: 1.5em; - aspect-ratio: 1 / 1; - object-fit: contain; -} diff --git a/client/src/components/AppHeader/AppHeader.jsx b/client/src/components/AppHeader/AppHeader.jsx deleted file mode 100644 index 0c3b788..0000000 --- a/client/src/components/AppHeader/AppHeader.jsx +++ /dev/null @@ -1,35 +0,0 @@ -import React from 'react'; -import AppHeaderAuth from '../AppHeaderAuth/AppHeaderAuth'; - -import logo from '../../images/microsoft_small.png'; - -import './AppHeader.css'; - -export default function AppHeader() { - return ( -
- - -
- ); -}; diff --git a/client/src/components/AppHeaderAuth/AppHeaderAuth.jsx b/client/src/components/AppHeaderAuth/AppHeaderAuth.jsx deleted file mode 100644 index 14414d7..0000000 --- a/client/src/components/AppHeaderAuth/AppHeaderAuth.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from 'react'; - -// React Context for Auth -import { useAuth } from '../../contexts/AuthContext'; - -export default function AppHeaderAuth() { - // React Context: User Authentication - const user = useAuth(); - - // Dynamically update auth div based on user context - const authElement = document.querySelector('.auth'); - if (authElement) { - // Default sign in - let html = 'Sign In'; - - // User profile and sign out - let clientPrincipal = (user && user.clientPrincipal) || null, - userDetails = (clientPrincipal && clientPrincipal.userDetails) || null; - - if (userDetails) { - html = `${userDetails} | Sign Out`; - } - - authElement.innerHTML = html; - } - - return ( -
- ); -}; diff --git a/client/src/components/Facets/CheckboxFacet/CheckboxFacet.css b/client/src/components/Facets/CheckboxFacet/CheckboxFacet.css deleted file mode 100644 index 992638d..0000000 --- a/client/src/components/Facets/CheckboxFacet/CheckboxFacet.css +++ /dev/null @@ -1,28 +0,0 @@ -.facet-checkbox { - list-style-type: none; - padding: 0; - margin: 0; -} - -.card-body { - padding-left: 0px; -} - -.facet-header:hover { - text-decoration: underline; - cursor: pointer; -} - -.facet-list-item { - padding-left: 36px !important; -} - -.facet-value-list-item { - padding-left: 46px !important; -} - -.facet-values-list { - max-height: 340px; - overflow-y: auto !important; - margin-right: 18px !important; -} \ No newline at end of file diff --git a/client/src/components/Facets/CheckboxFacet/CheckboxFacet.jsx b/client/src/components/Facets/CheckboxFacet/CheckboxFacet.jsx deleted file mode 100644 index 0950354..0000000 --- a/client/src/components/Facets/CheckboxFacet/CheckboxFacet.jsx +++ /dev/null @@ -1,53 +0,0 @@ -import React, { useState } from 'react'; -import { Collapse, Checkbox, List, ListItem, ListItemText } from '@mui/material'; -import { ExpandLess, ExpandMore } from '@mui/icons-material'; - -import './CheckboxFacet.css'; - -export default function CheckboxFacet(props) { - const [isExpanded, setIsExpanded] = useState(false); - - const checkboxes = props.values.map(facetValue => { - let isSelected = props.selectedFacets.some(facet => facet.value === facetValue.value); - - return ( - - props.removeFilter({field: props.name, value: facetValue.value}) - : () => props.addFilter(props.name, facetValue.value) - } - /> - - - ); - }); - - return ( -
- setIsExpanded(!isExpanded)} - className="facet-list-item" - > - - {isExpanded ? : } - - - - {checkboxes} - - -
- ); -} \ No newline at end of file diff --git a/client/src/components/Facets/CheckboxFacet/CheckboxFacet.tsx b/client/src/components/Facets/CheckboxFacet/CheckboxFacet.tsx new file mode 100644 index 0000000..1335184 --- /dev/null +++ b/client/src/components/Facets/CheckboxFacet/CheckboxFacet.tsx @@ -0,0 +1,72 @@ +import { useState } from 'react'; + +import Box from '@mui/material/Box'; +import { + FacetListItem, + FacetValueItem, + FacetList, + CustomCheckbox, + ExpandIcon +} from './styles.jsx'; + +export default function CheckboxFacet(props) { + const [isExpanded, setIsExpanded] = useState(false); + + const checkboxes = props.values.map(facetValue => { + let isSelected = props.selectedFacets.some(facet => facet.value === facetValue.value); + + const handleClick = () => { + if (isSelected) { + props.removeFilter({field: props.name, value: facetValue.value}); + } else { + props.addFilter(props.name, facetValue.value); + } + }; + + return ( +
  • + + + + {`${facetValue.value} (${facetValue.count})`} + + +
  • + ); + }); + + // Simple animation styles for collapse effect + const collapseStyle = { + maxHeight: isExpanded ? '1000px' : '0', + overflow: 'hidden', + transition: 'max-height 0.3s ease-in-out', + }; + + return ( + + setIsExpanded(!isExpanded)} + style={{ fontWeight: 500 }} + > + + {props.mapFacetName(props.name)} + + + +
    + + {checkboxes} + +
    +
    + ); +} \ No newline at end of file diff --git a/client/src/components/Facets/CheckboxFacet/styles.tsx b/client/src/components/Facets/CheckboxFacet/styles.tsx new file mode 100644 index 0000000..0e47b50 --- /dev/null +++ b/client/src/components/Facets/CheckboxFacet/styles.tsx @@ -0,0 +1,73 @@ +import { styled } from '@mui/material/styles'; + +// Using lightweight styled components instead of MUI components +export const FacetListItem = styled('div')(() => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'space-between', + padding: '8px 16px 8px 32px', + cursor: 'pointer', + '&:hover': { + backgroundColor: 'rgba(0, 0, 0, 0.04)', + }, +})); + +export const FacetValueItem = styled('div')(() => ({ + display: 'flex', + alignItems: 'center', + padding: '4px 16px 4px 48px', + cursor: 'pointer', + '&:hover': { + backgroundColor: 'rgba(0, 0, 0, 0.04)', + }, +})); + +export const FacetList = styled('ul')(() => ({ + maxHeight: '340px', + overflowY: 'auto', + paddingRight: '16px', + margin: 0, + listStyle: 'none', +})); + +// Custom checkbox component +export const CustomCheckbox = styled('div')<{ checked: boolean }>(({ checked }) => ({ + width: '18px', + height: '18px', + border: checked ? '2px solid #1976d2' : '2px solid rgba(0, 0, 0, 0.6)', + borderRadius: '2px', + marginRight: '8px', + position: 'relative', + backgroundColor: checked ? '#1976d2' : 'transparent', + '&::after': checked ? { + content: '""', + position: 'absolute', + left: '4px', + top: '1px', + width: '6px', + height: '10px', + border: 'solid white', + borderWidth: '0 2px 2px 0', + transform: 'rotate(45deg)', + } : {}, +})); + +// Lightweight expand/collapse icons +export const ExpandIcon = ({ expanded }: { expanded: boolean }) => ( +
    + + + +
    +); \ No newline at end of file diff --git a/client/src/components/Facets/Facets.css b/client/src/components/Facets/Facets.css deleted file mode 100644 index 2dc0526..0000000 --- a/client/src/components/Facets/Facets.css +++ /dev/null @@ -1,23 +0,0 @@ -.facetbox { - border-right: 1px solid #f0f0f0; -} - -.box { - height: 100%; -} - -.listitem { - padding-left: 36px !important; -} - -.filterlist { - list-style: none; -} - -.chip { - margin: 0.25em; -} - -.facet-list { - margin-top: 32px !important; -} \ No newline at end of file diff --git a/client/src/components/Facets/Facets.jsx b/client/src/components/Facets/Facets.tsx similarity index 66% rename from client/src/components/Facets/Facets.jsx rename to client/src/components/Facets/Facets.tsx index e969544..496782f 100644 --- a/client/src/components/Facets/Facets.jsx +++ b/client/src/components/Facets/Facets.tsx @@ -1,8 +1,13 @@ -import React from 'react'; -import { List, Chip } from '@mui/material'; -// Removed styled import now that we no longer use styled-components +import Box from '@mui/material/Box'; import CheckboxFacet from './CheckboxFacet/CheckboxFacet'; -import "./Facets.css"; +import { + StyledChip, + ChipLabel, + ChipDeleteButton, + FacetBox, + FilterList, + FacetList +} from './styles.jsx'; export default function Facets(props) { @@ -46,27 +51,26 @@ export default function Facets(props) { const filters = props.filters.map((filter, index) => { return (
  • - removeFilter(filter)} - className="chip" - /> + + {`${mapFacetName(filter.field)}: ${filter.value}`} + removeFilter(filter)}>× +
  • ); }); return ( -
    -
    -
    -
      + + +
      + {filters} -
    +
    - + {facets} - -
    -
    + + +
    ); } \ No newline at end of file diff --git a/client/src/components/Facets/styles.tsx b/client/src/components/Facets/styles.tsx new file mode 100644 index 0000000..a690326 --- /dev/null +++ b/client/src/components/Facets/styles.tsx @@ -0,0 +1,60 @@ +import { styled } from '@mui/material/styles'; +// Styled components using basic HTML elements to reduce bundle size +export const FacetBox = styled('div')(() => ({ + height: '100%', + boxShadow: 'none', + backgroundColor: 'transparent', + borderRadius: 0, +})); + +export const FilterList = styled('ul')(() => ({ + display: 'flex', + flexWrap: 'wrap', + padding: '8px 0', + margin: 0, + listStyle: 'none', +})); + +// Custom lightweight chip component instead of MUI Chip +export const StyledChip = styled('div')(() => ({ + display: 'flex', + alignItems: 'center', + height: '32px', + margin: '4px', + padding: '0 12px', + fontSize: '0.8125rem', + backgroundColor: '#e0e0e0', + borderRadius: '16px', + cursor: 'default', + '&:hover': { + backgroundColor: '#bdbdbd', + }, +})); + +export const ChipLabel = styled('span')(() => ({ + padding: '0 8px 0 0', +})); + +export const ChipDeleteButton = styled('button')(() => ({ + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + width: '16px', + height: '16px', + padding: 0, + fontSize: '14px', + lineHeight: 1, + color: '#666', + backgroundColor: 'transparent', + border: 'none', + borderRadius: '50%', + cursor: 'pointer', + '&:hover': { + backgroundColor: 'rgba(0, 0, 0, 0.1)', + }, +})); + +export const FacetList = styled('nav')(() => ({ + marginTop: '32px', + padding: 0, +})); \ No newline at end of file diff --git a/client/src/components/Pager.tsx b/client/src/components/Pager.tsx new file mode 100644 index 0000000..9f198e0 --- /dev/null +++ b/client/src/components/Pager.tsx @@ -0,0 +1,89 @@ +import Pagination from '@mui/material/Pagination'; +import Box from '@mui/material/Box'; +import { styled } from '@mui/material/styles'; + +// Use Material UI's styled API for better style isolation +const StyledPagination = styled(Pagination)(() => ({ + margin: '1em auto', + '& .MuiPaginationItem-root': { + color: '#0078d4', + }, + '& .MuiPaginationItem-page.Mui-selected': { + backgroundColor: '#0078d4', + color: 'white', + '&:hover': { + backgroundColor: '#106ebe', + } + } +})); + +// Constants +const PAGE_WINDOW = 2; // Pages to show before and after the current page + +/** + * Pagination component for search results - fully controlled by parent + * @param {Object} props + * @param {number} props.currentPage - Current active page number (1-based) + * @param {number} props.resultCount - Total number of results across all pages + * @param {number} props.resultsPerPage - Number of results displayed per page + * @param {function} props.onPageChange - Callback function when page changes + */ +export default function Pager(props) { + // Destructure props for cleaner code and proper dependency tracking + const { currentPage, resultCount, resultsPerPage, onPageChange } = props; + + // Ensure currentPage is always an integer + const page = parseInt(currentPage) || 1; + const totalPages = Math.max(1, Math.ceil(resultCount / resultsPerPage)); + + // Handler for changing the current page + function handlePageChange(pageNumber) { + // Convert to integer and clamp within valid range + const newPage = Math.max(1, Math.min(totalPages, parseInt(pageNumber) || 1)); + + // Only update if actually changing page + if (newPage !== page) { + onPageChange(newPage); + } + } + + // With Material UI's Pagination component, we don't need the custom logic for + // next/previous page navigation or calculating page windows, as these are + // handled internally by the Pagination component + + // With Material UI's Pagination component, we don't need to manually render page links + // or previous/next buttons, as they're handled by the component itself + + // We also don't need the page window calculation for rendering since + // Material UI's Pagination handles this with siblingCount and boundaryCount props + + // Handle case with no results + if (totalPages <= 0) { + return null; // No pagination needed when there are no results + } + + return ( + + handlePageChange(newPage)} + showFirstButton + showLastButton + siblingCount={PAGE_WINDOW} + boundaryCount={1} + /> + + ); +} diff --git a/client/src/components/Pager/Pager.css b/client/src/components/Pager/Pager.css deleted file mode 100644 index de4d498..0000000 --- a/client/src/components/Pager/Pager.css +++ /dev/null @@ -1,8 +0,0 @@ -.item { - margin: 1em auto; -} - -.pager { - margin: auto; - max-width: fit-content; -} \ No newline at end of file diff --git a/client/src/components/Pager/Pager.jsx b/client/src/components/Pager/Pager.jsx deleted file mode 100644 index 5820b81..0000000 --- a/client/src/components/Pager/Pager.jsx +++ /dev/null @@ -1,164 +0,0 @@ -import React, { useMemo } from 'react'; -import PropTypes from 'prop-types'; - -import './Pager.css'; - -// Constants -const PAGE_WINDOW = 2; // Pages to show before and after the current page - -/** - * Pagination component for search results - fully controlled by parent - * @param {Object} props - * @param {number} props.currentPage - Current active page number (1-based) - * @param {number} props.resultCount - Total number of results across all pages - * @param {number} props.resultsPerPage - Number of results displayed per page - * @param {function} props.onPageChange - Callback function when page changes - */ -export default function Pager(props) { - // Destructure props for cleaner code and proper dependency tracking - const { currentPage, resultCount, resultsPerPage, onPageChange } = props; - - // Ensure currentPage is always an integer - const page = parseInt(currentPage) || 1; - const totalPages = Math.max(1, Math.ceil(resultCount / resultsPerPage)); - - // Handler for changing the current page - function handlePageChange(pageNumber) { - // Convert to integer and clamp within valid range - const newPage = Math.max(1, Math.min(totalPages, parseInt(pageNumber) || 1)); - - // Only update if actually changing page - if (newPage !== page) { - onPageChange(newPage); - } - } - - // Handler for next page button click - function handleNextPage() { - if (page < totalPages) { - handlePageChange(page + 1); - } - } - - // Handler for previous page button click - function handlePreviousPage() { - if (page > 1) { - handlePageChange(page - 1); - } - } - - // Calculate page range and memoize to avoid recalculation on every render - const { minPage, maxPage } = useMemo(() => { - let minPage = Math.max(1, page - PAGE_WINDOW); - let maxPage = Math.min(totalPages, page + PAGE_WINDOW); - - // Adjust range if we're near the start or end - // This ensures we always show 5 pages if available - if (maxPage - minPage < PAGE_WINDOW * 2) { - if (page < totalPages / 2) { - // Near start, expand end - maxPage = Math.min(totalPages, minPage + PAGE_WINDOW * 2); - } else { - // Near end, expand start - minPage = Math.max(1, maxPage - PAGE_WINDOW * 2); - } - } - - return { minPage, maxPage }; - }, [page, totalPages]); - - // Generate page links array - function renderPageLinks() { - const links = []; - - for (let i = minPage; i <= maxPage; i++) { - if (i === page) { - links.push( -
  • - - {i} - -
  • - ); - } else { - links.push( -
  • - -
  • - ); - } - } - return links; - } - - // Create previous button component - function renderPreviousButton() { - const isFirstPage = page === 1; - return ( -
  • - {isFirstPage ? ( - Previous - ) : ( - - )} -
  • - ); - } - - // Create next button component - function renderNextButton() { - const isLastPage = page === totalPages; - return ( -
  • - {isLastPage ? ( - Next - ) : ( - - )} -
  • - ); - } - - // Handle case with no results - if (totalPages <= 0) { - return null; // No pagination needed when there are no results - } - - return ( - - ); -} - -// PropTypes for better documentation and runtime type checking -Pager.propTypes = { - currentPage: PropTypes.number, - resultCount: PropTypes.number.isRequired, - resultsPerPage: PropTypes.number.isRequired, - onPageChange: PropTypes.func.isRequired -}; - -// Default props -Pager.defaultProps = { - currentPage: 1 -}; diff --git a/client/src/components/Results/Result/Result.css b/client/src/components/Results/Result/Result.css deleted file mode 100644 index 91aed91..0000000 --- a/client/src/components/Results/Result/Result.css +++ /dev/null @@ -1,54 +0,0 @@ -.result { - width: 300px; - padding: 8px; - text-align: center; - border: 1px solid #eee; - box-shadow: 0 2px 3px #ccc; - margin: 10px; - margin-bottom: 3px; - padding-bottom: 3px; - box-sizing: border-box; - cursor: pointer; -} -.card-img-top { - width: 100%; /* Ensure the image takes up the full width of the container */ - height: 150px; /* Set a fixed height for uniformity */ - object-fit: contain; /* Ensure the image scales and crops to fit the box */ - display: block; - margin: 0 auto; - background-color: #f0f0f0; /* Optional: Add a background color for empty space */ -} -.result:hover, -.result:active { - background-color: #C0DDF5; -} - -.title-style { - font-size: 0.9em; - vertical-align: middle; - white-space: normal; /* Allow wrapping to multiple lines */ - overflow: hidden; - text-overflow: ellipsis; - display: -webkit-box; /* Use flexbox for multi-line ellipsis */ - -webkit-line-clamp: 2; /* Limit to 2 lines */ - -webkit-box-orient: vertical; - line-height: 1.4em; /* Adjust line height for better spacing */ - max-height: 2.8em; /* Ensure height fits 2 lines */ - margin: 0; /* Remove any extra margin */ - color: #0078d7; -} - -.card-body { - padding: 0 !important; - margin: 0; - text-align: center; - height: 3.2em; /* Fixed height to support 2 rows of text */ - display: flex; /* Use flexbox for alignment */ - justify-content: center; /* Center the text horizontally */ - align-items: center; /* Center the text vertically */ - box-shadow: none !important; - border: none !important; -} -.result a { - text-decoration: none; /* Remove underline from links */ -} diff --git a/client/src/components/Results/Result/Result.jsx b/client/src/components/Results/Result/Result.jsx deleted file mode 100644 index a46957c..0000000 --- a/client/src/components/Results/Result/Result.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from 'react'; - -import './Result.css'; - -export default function Result(props) { - const title = props.document.original_title || ''; - - return( -
    - - {props.document.original_title} -
    -
    {title}
    -
    -
    -
    - ); -} diff --git a/client/src/components/Results/Result/Result.tsx b/client/src/components/Results/Result/Result.tsx new file mode 100644 index 0000000..778beb3 --- /dev/null +++ b/client/src/components/Results/Result/Result.tsx @@ -0,0 +1,49 @@ +import Box from '@mui/material/Box'; +import { ResultProps } from '../../../types/props'; +import { + ResultCard, + ResultImage, + TitleText +} from './styled.jsx'; + +export default function Result(props: ResultProps) { + const title = props.document.original_title || ''; + + + console.log(props.document); + + return ( + + + + {/* Using div with inline styles instead of Box component to reduce bundle size */} +
    + {/* Image section - fixed height */} +
    + +
    + + {/* Text section - centered in remaining space */} +
    + + {title} + +
    +
    +
    +
    +
    + ); +} diff --git a/client/src/components/Results/Result/styled.tsx b/client/src/components/Results/Result/styled.tsx new file mode 100644 index 0000000..c5ec16d --- /dev/null +++ b/client/src/components/Results/Result/styled.tsx @@ -0,0 +1,47 @@ +import { styled } from '@mui/material/styles'; +// Styled div instead of Card to reduce bundle size +export const ResultCard = styled('div')(() => ({ + width: '200px', + padding: '8px', + textAlign: 'center', + border: '1px solid #eee', + boxShadow: '0 2px 3px #ccc', + margin: '10px', + marginBottom: '3px', + paddingBottom: '8px', + cursor: 'pointer', + transition: 'background-color 0.3s', + height: '220px', // Fixed height for consistent card sizing + display: 'flex', + flexDirection: 'column', + overflow: 'hidden', // Prevent content overflow + '&:hover': { + backgroundColor: '#C0DDF5', + }, +})); + +// Styled img instead of CardMedia to reduce bundle size +export const ResultImage = styled('img')(() => ({ + width: '100%', + height: '100%', + objectFit: 'contain', + display: 'block', + margin: '0 auto', + backgroundColor: '#ffffff', +})); + +// Styled div instead of Typography to reduce bundle size +export const TitleText = styled('div')(() => ({ + fontSize: '0.9em', + overflow: 'hidden', + textOverflow: 'ellipsis', + display: '-webkit-box', + WebkitLineClamp: 2, + WebkitBoxOrient: 'vertical', + lineHeight: '1.4em', + minHeight: '2.8em', // Force minimum height for 2 lines + color: '#0078d7', + padding: '0 8px', + textAlign: 'center', + width: '100%' +})); \ No newline at end of file diff --git a/client/src/components/Results/Results.css b/client/src/components/Results/Results.css deleted file mode 100644 index 623ed43..0000000 --- a/client/src/components/Results/Results.css +++ /dev/null @@ -1,19 +0,0 @@ -/* Detail Styles */ -.results-info { - margin: 1em; -} - -.results { - display: flex; - flex-flow: row wrap; - justify-content: center; - width: 100%; - margin: auto; - margin-left: 0em; - margin-right: 0em; -} - -.results .result { - width: 10rem; - max-height: 18rem; -} \ No newline at end of file diff --git a/client/src/components/Results/Results.jsx b/client/src/components/Results/Results.jsx deleted file mode 100644 index dfb5b6b..0000000 --- a/client/src/components/Results/Results.jsx +++ /dev/null @@ -1,26 +0,0 @@ -import React from 'react'; -import Result from './Result/Result'; - -import "./Results.css"; - -export default function Results(props) { - - let results = props.documents.map((result, index) => { - return ; - }); - - let beginDocNumber = Math.min(props.skip + 1, props.count); - let endDocNumber = Math.min(props.skip + props.top, props.count); - - return ( -
    -

    Showing {beginDocNumber}-{endDocNumber} of {props.count.toLocaleString()} results for {props.query}

    -
    - {results} -
    -
    - ); -}; diff --git a/client/src/components/Results/Results.tsx b/client/src/components/Results/Results.tsx new file mode 100644 index 0000000..dee7c9e --- /dev/null +++ b/client/src/components/Results/Results.tsx @@ -0,0 +1,50 @@ +import Box from '@mui/material/Box'; +import Result from './Result/Result'; +import { ResultsProps } from '../../types/props'; +import { + ResultsContainer, + ResultsInfo +} from './styled'; + +export default function Results(props: ResultsProps) { + + let results = props.searchResultDocuments.map((result, index) => { + + let book = result?.document; + + return ; + }); + + console.log(results[0]); + + // Provide default values for pagination properties + const skip = props.skip ?? 0; // Default to 0 if skip is not provided + const count = props.count ?? 0; // Default to 0 if count is not provided + const top = props.top ?? 8; // Default to 8 if top is not provided + + // When there are results, show 1-based counting for beginDocNumber + // When no results, beginDocNumber should be 0 + let beginDocNumber = count > 0 ? skip + 1 : 0; + + // For endDocNumber, take the smaller of (skip + top) or count + // This ensures we don't show ranges beyond the actual number of results + let endDocNumber = count > 0 ? Math.min(skip + top, count) : 0; + + return ( + + + {count > 0 ? ( + <>Showing {beginDocNumber}-{endDocNumber} of {count.toLocaleString()} results for {props.query ?? ""} + ) : ( + <>No results found for {props.query ?? ""} + )} + + + {results} + + + ); +}; diff --git a/client/src/components/Results/styled.tsx b/client/src/components/Results/styled.tsx new file mode 100644 index 0000000..77e67c2 --- /dev/null +++ b/client/src/components/Results/styled.tsx @@ -0,0 +1,21 @@ +import { styled } from '@mui/material/styles'; +import Grid from '@mui/material/Grid'; +import Typography from '@mui/material/Typography'; + +// Styled components for MUI isolation +export const ResultsContainer = styled(Grid)(() => ({ + display: 'flex', + flexFlow: 'row wrap', + justifyContent: 'center', + width: '100%', + margin: 'auto', + '& > *': { + height: 'auto', // Allow natural height + alignSelf: 'stretch' // Make each grid item stretch to fill its cell + } +})); + +export const ResultsInfo = styled(Typography)(() => ({ + margin: '1em', + fontFamily: '"Roboto", "Helvetica", "Arial", sans-serif', +})); \ No newline at end of file diff --git a/client/src/components/SearchBar/SearchBar.css b/client/src/components/SearchBar/SearchBar.css deleted file mode 100644 index 32fae97..0000000 --- a/client/src/components/SearchBar/SearchBar.css +++ /dev/null @@ -1,46 +0,0 @@ -.search-button { - font-size: 14px; - margin-left: 10px; - border-color: 'green' -} - -.suggestions { - position: relative; - display: inline-block; - width: inherit; - z-index:99 -} - -.search-bar { - flex-wrap: nowrap; - display: flex; - justify-content: center; - align-items: center; - margin: 0 auto; -} -.search-bar-narrow { - - -} - -.search-bar-wide { - width: 40%; - margin-left: 5px; -} - -/* Moved styles from SearchBar.jsx */ -.autocomplete { - width: 75%; -} - -.autocomplete .MuiAutocomplete-endAdornment { - display: none; -} - -/* Added styles for the Box component */ -.search-bar-box { - display: flex; - align-items: center; - flex: 1; -} - diff --git a/client/src/components/SearchBar/SearchBar.jsx b/client/src/components/SearchBar/SearchBar.jsx deleted file mode 100644 index c3900cd..0000000 --- a/client/src/components/SearchBar/SearchBar.jsx +++ /dev/null @@ -1,84 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { TextField, Autocomplete, Button, Box } from '@mui/material'; -import fetchInstance from '../../url-fetch'; -import './SearchBar.css'; - -export default function SearchBar({ postSearchHandler, query, width }) { - const [q, setQ] = useState(() => query || ''); - const [suggestions, setSuggestions] = useState([]); - - const search = (value) => { - postSearchHandler(value); - }; - - useEffect(() => { - if (q) { - - const body = { q, top: 5, suggester: 'sg' }; - - fetchInstance('/api/suggest', { body, method: 'POST' }) - .then(response => { - setSuggestions(response.suggestions.map(s => s.text)); - }) - .catch(error => { - console.log(error); - setSuggestions([]); - }); - } - }, [q]); - - - const onInputChangeHandler = (event, value) => { - setQ(value); - }; - - - const onChangeHandler = (event, value) => { - - setQ(value); - search(value); - }; - - const onEnterButton = (event) => { - // if enter key is pressed - if (event.key === 'Enter') { - search(q); - } - }; - - return ( -
    - - ( - setSuggestions([])} - onClick={() => setSuggestions([])} - /> - )} - /> -
    - -
    -
    -
    - ); -} \ No newline at end of file diff --git a/client/src/components/SearchBar/SearchBar.tsx b/client/src/components/SearchBar/SearchBar.tsx new file mode 100644 index 0000000..c4fb80e --- /dev/null +++ b/client/src/components/SearchBar/SearchBar.tsx @@ -0,0 +1,96 @@ +import { useState, useEffect, useRef, KeyboardEvent, ChangeEvent } from 'react'; +import Box from '@mui/material/Box'; +import fetchInstance from '../../url-fetch'; +import { SearchBarProps } from '../../types/props'; +import { SuggestRequest, SuggestResponse } from '../../types/api'; +import { + SearchContainer, + SearchBox, + SearchInput, + SearchButton, + SuggestionList, + SuggestionItem +} from './styles'; + +export default function SearchBar({ postSearchHandler, query, width }: SearchBarProps) { + const [q, setQ] = useState(() => query || ''); + const [suggestions, setSuggestions] = useState([]); + + const search = (value: string): void => { + postSearchHandler(value); + }; + + useEffect(() => { + if (q) { + const body: SuggestRequest = { q, top: 5, suggester: 'sg' }; + + fetchInstance('/api/suggest', { body, method: 'POST' }) + .then((response: SuggestResponse) => { + setSuggestions(response.suggestions.map((s) => s.text)); + }) + .catch((error: Error) => { + console.log(error); + setSuggestions([]); + }); + } else { + setSuggestions([]); + } + }, [q]); + + // Handle enter key in the search field + const handleKeyPress = (event: KeyboardEvent): void => { + if (event.key === 'Enter') { + search(q); + } + }; + + const [showSuggestions, setShowSuggestions] = useState(false); + const inputRef = useRef(null); + + // Handle selecting suggestion + const handleSuggestionClick = (suggestion: string): void => { + setQ(suggestion); + search(suggestion); + setShowSuggestions(false); + }; + + return ( + + + +
    + ) => setQ(e.target.value)} + onFocus={() => setShowSuggestions(true)} + onBlur={() => { + // Delay to allow click on suggestion + setTimeout(() => setShowSuggestions(false), 200); + }} + onKeyPress={handleKeyPress} + id="search-box" + placeholder="What are you looking for?" + style={{ width: '100%' }} + /> + {showSuggestions && suggestions.length > 0 && ( + + {suggestions.map((suggestion: string, index: number) => ( + handleSuggestionClick(suggestion)} + > + {suggestion} + + ))} + + )} +
    + search(q)}> + Search + +
    +
    +
    + ); +} \ No newline at end of file diff --git a/client/src/components/SearchBar/styles.tsx b/client/src/components/SearchBar/styles.tsx new file mode 100644 index 0000000..d27ee66 --- /dev/null +++ b/client/src/components/SearchBar/styles.tsx @@ -0,0 +1,75 @@ +import { styled } from '@mui/material/styles'; +// Using styled HTML elements instead of MUI components to reduce bundle size +export const SearchContainer = styled('div')(() => ({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + margin: '0 auto', + width: '100%', +})); + +export const SearchBox = styled('div')(() => ({ + display: 'flex', + alignItems: 'center', + width: '100%', + // Make sure the input field takes up most of the space + '& > div': { + flexGrow: 1, + width: 'calc(100% - 100px)', // Accounting for button width + margin + } +})); + +// Custom lightweight autocomplete implementation +export const SearchInput = styled('input')(() => ({ + width: '100%', + padding: '10px 14px', + fontSize: '16px', + borderRadius: '4px', + border: '1px solid #ccc', + outline: 'none', + height: '40px', + boxSizing: 'border-box', + '&:focus': { + borderColor: '#1976d2', + boxShadow: '0 0 0 2px rgba(25, 118, 210, 0.2)' + } +})); + +export const SuggestionList = styled('ul')(() => ({ + position: 'absolute', + zIndex: 1000, + background: 'white', + width: '100%', + maxHeight: '200px', + overflowY: 'auto', + borderRadius: '4px', + boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', + margin: 0, + padding: 0, + listStyle: 'none', +})); + +export const SuggestionItem = styled('li')(() => ({ + padding: '8px 14px', + cursor: 'pointer', + '&:hover': { + backgroundColor: '#f5f5f5' + } +})); + +export const SearchButton = styled('button')(() => ({ + marginLeft: '8px', + height: '40px', + minWidth: '80px', // Fixed minimum width for the button + padding: '0 16px', + backgroundColor: '#1976d2', + color: 'white', + border: 'none', + borderRadius: '4px', + fontWeight: 500, + cursor: 'pointer', + flexShrink: 0, // Prevent button from shrinking + '&:hover': { + backgroundColor: '#1565c0' + } +})); \ No newline at end of file diff --git a/client/src/contexts/AuthContext.jsx b/client/src/contexts/AuthContext.jsx deleted file mode 100644 index dcb883b..0000000 --- a/client/src/contexts/AuthContext.jsx +++ /dev/null @@ -1,9 +0,0 @@ -import { createContext, useContext } from 'react'; - -// Create new auth context -export const AuthContext = createContext(); - -// React hook to use auth context -export function useAuth() { - return useContext(AuthContext); -} diff --git a/client/src/debug.ts b/client/src/debug.ts new file mode 100644 index 0000000..581044f --- /dev/null +++ b/client/src/debug.ts @@ -0,0 +1,52 @@ +/** + * Debug utility for styling components + * Used to visualize component boundaries during development + */ + +// Set this to true to enable debug mode +export const DEBUG_MODE = false; + +// Debug styles to be imported and used in your app +export const debugStyles = DEBUG_MODE + ? { + // Debug styles for all elements + '*': { + outline: '1px solid red', + }, + + // Debug styles specifically for MUI components + '.MuiBox-root': { + outline: '1px solid blue !important', + }, + '.MuiGrid-root': { + outline: '1px solid green !important', + }, + '.MuiContainer-root': { + outline: '1px solid purple !important', + }, + + // Debug styles for basic elements + 'div': { + outline: '1px dashed red', + }, + 'section': { + outline: '1px dashed orange', + }, + 'header, footer': { + outline: '2px solid magenta', + }, + + // Optional - Add component name label to top-left corner of selected elements + '.MuiBox-root:before, .MuiGrid-root:before, .MuiContainer-root:before': { + content: 'attr(class)', + position: 'absolute', + top: '0', + left: '0', + fontSize: '10px', + backgroundColor: 'rgba(255, 255, 255, 0.8)', + padding: '2px', + color: 'black', + zIndex: 1000, + }, + } + : {}; // Empty object when debug mode is off diff --git a/client/src/index.css b/client/src/index.css deleted file mode 100644 index a78f33f..0000000 --- a/client/src/index.css +++ /dev/null @@ -1,69 +0,0 @@ -:root { - font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; - line-height: 1.5; - font-weight: 400; - - color-scheme: light dark; - color: rgba(255, 255, 255, 0.87); - background-color: #242424; - - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} - -a { - font-weight: 500; - color: #646cff; - text-decoration: inherit; -} -a:hover { - color: #535bf2; -} - -body { - margin: 0; - display: flex; - place-items: center; - min-width: 320px; - min-height: 100vh; - padding: 0; -} - -h1 { - font-size: 3.2em; - line-height: 1.1; -} - -button { - border-radius: 8px; - border: 1px solid transparent; - padding: 0.6em 1.2em; - font-size: 1em; - font-weight: 500; - font-family: inherit; - background-color: #1a1a1a; - cursor: pointer; - transition: border-color 0.25s; -} -button:hover { - border-color: #646cff; -} -button:focus, -button:focus-visible { - outline: 4px auto -webkit-focus-ring-color; -} - -@media (prefers-color-scheme: light) { - :root { - color: #213547; - background-color: #ffffff; - } - a:hover { - color: #747bff; - } - button { - background-color: #f9f9f9; - } -} diff --git a/client/src/main.jsx b/client/src/main.jsx deleted file mode 100644 index fb92537..0000000 --- a/client/src/main.jsx +++ /dev/null @@ -1,13 +0,0 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './App/App.css' -import App from './App/App.jsx' - - -console.log('client2'); - -createRoot(document.getElementById('root')).render( - - - , -) diff --git a/client/src/main.tsx b/client/src/main.tsx new file mode 100644 index 0000000..55a2a2b --- /dev/null +++ b/client/src/main.tsx @@ -0,0 +1,22 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { ThemeProvider } from '@mui/material/styles' +import CssBaseline from '@mui/material/CssBaseline' +import GlobalStyles from '@mui/material/GlobalStyles' +import theme, { globalStyles } from './theme' +import App from './App/App' + +const rootElement = document.getElementById('root'); +if (!rootElement) { + throw new Error("Root element with id 'root' not found."); +} + +createRoot(rootElement).render( + + + + + + + +) \ No newline at end of file diff --git a/client/src/pages/Details/Details.css b/client/src/pages/Details/Details.css deleted file mode 100644 index cb5ac60..0000000 --- a/client/src/pages/Details/Details.css +++ /dev/null @@ -1,88 +0,0 @@ -.main--details { - padding-top: 2em; -} -.tab-panel{ - width: 100%; -} -.tab-panel-value{ - padding: 3; -} - -.image { - width: 10em; - height: auto; -} -.card-body { - height: inherit; /* Allow the card body to grow with content */ - position: relative; /* Ensure the card body is positioned relative to its container */ - background-color: #fff; /* Add a background color to the card */ - border: 1px solid #ddd; /* Add a border to define the card */ - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); /* Add a subtle shadow for depth */ - padding: 1em; /* Add padding for spacing */ - margin: 1em 0; /* Add margin to separate the card from other elements */ - display: flex; /* Use flexbox for layout */ - flex-direction: column; /* Stack elements vertically */ - align-items: center; /* Center elements horizontally */ - justify-content: flex-start; /* Align elements to the top */ - width: 100%; /* Ensure the card takes up the full width of the container */ - box-sizing: border-box; /* Include padding and border in the width/height */ -} - -.image { - width: 10em; /* Set a fixed width for the image */ - height: auto; /* Maintain the aspect ratio */ - margin-bottom: 1em; /* Add spacing below the image */ -} - -.card-title { - font-size: 1.2em; - font-weight: bold; - margin-bottom: 0.5em; - text-align: center; -} - -.card-text { - font-size: 1em; - margin-bottom: 0.5em; - text-align: center; -} - -.tab-panel { - position: relative; /* Ensure proper positioning */ - width: 100%; /* Take up full width */ - overflow: visible; /* Allow content to expand */ - background-color: #fff; /* Optional: Add a background color */ - padding: 1em; /* Add padding for spacing */ - box-sizing: border-box; /* Include padding and border in width/height */ - min-height: auto; /* Allow the height to grow dynamically */ -} - -/* Styles for the header box */ -.box-header { - border-bottom: 1px solid var(--divider-color); - padding: 1em; - background-color: #f5f5f5; -} - -/* Styles for the content box */ -.box-content { - padding: 1.5em; - background-color: #ffffff; - border: 1px solid #ddd; - box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); -} - -.details-box-parent { - width: 100%; - padding-top: 25px; - padding-left: 150px; - padding-right: 150px; -} -.details-tab-box-header { - border-bottom: 1px solid; - border-color: var(--divider-color); -} -.details-custom-tab-panel-json-div { - text-align: left; -} - diff --git a/client/src/pages/Details/Details.jsx b/client/src/pages/Details/Details.jsx deleted file mode 100644 index 767ce81..0000000 --- a/client/src/pages/Details/Details.jsx +++ /dev/null @@ -1,95 +0,0 @@ -import React, { useState, useEffect } from "react"; -import { useParams } from 'react-router-dom'; -import Rating from '@mui/material/Rating'; -import CircularProgress from '@mui/material/CircularProgress'; -import Tabs from '@mui/material/Tabs'; -import Tab from '@mui/material/Tab'; -import Box from '@mui/material/Box'; - -import fetchInstance from '../../url-fetch'; - -import "./Details.css"; - - -function CustomTabPanel(props) { - const { children, value, index, ...other } = props; - - return ( - - ); -} - -export default function BasicTabs() { - const { id } = useParams(); - const [document, setDocument] = useState({}); - const [value, setValue] = React.useState(0); - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - setIsLoading(true); - fetchInstance('/api/lookup', { query: { id } }) - .then(response => { - console.log(JSON.stringify(response)) - const doc = response.document; - setDocument(doc); - setIsLoading(false); - }) - .catch(error => { - console.log(error); - setIsLoading(false); - }); - - }, [id]); - - const handleChange = (event, newValue) => { - setValue(newValue); - }; - - - if (isLoading || !id || Object.keys(document).length === 0) { - return ( -
    - -

    Loading...

    -
    - ); - } - - return ( - - - - - - - - -
    -
    {document.original_title}
    - Book cover -

    {document.authors?.join('; ')} - {document.original_publication_year}

    -

    ISBN {document.isbn}

    - -

    {document.ratings_count} Ratings

    -
    -
    - -
    -
    
    -            {JSON.stringify(document, null, 2)}
    -          
    -
    -
    -
    - ); -} \ No newline at end of file diff --git a/client/src/pages/Details/Details.tsx b/client/src/pages/Details/Details.tsx new file mode 100644 index 0000000..31e58c6 --- /dev/null +++ b/client/src/pages/Details/Details.tsx @@ -0,0 +1,115 @@ +import React, { useState, useEffect } from "react"; +import { useParams } from 'react-router-dom'; +import Rating from '@mui/material/Rating'; +import CircularProgress from '@mui/material/CircularProgress'; +import Tabs from '@mui/material/Tabs'; +import Tab from '@mui/material/Tab'; +import Box from '@mui/material/Box'; + +import fetchInstance from '../../url-fetch'; +import { Document } from '../../types/models'; + +import { + TabPanel, + TabPanelValue, + CardBody, + ImageContainer, + CardTitle, + CardText, + BoxContent, + DetailsBoxParent, + DetailsTabBoxHeader, + DetailsCustomTabPanelJsonDiv +} from './styled'; + + +interface CustomTabPanelProps { + children?: React.ReactNode; + value: number; + index: number; + component?: React.ElementType; + [key: string]: any; // For other props +} + +function CustomTabPanel(props: CustomTabPanelProps) { + const { children, value, index, ...other } = props; + + return ( + + ); +} + +export default function BasicTabs() { + const { id } = useParams(); + const [document, setDocument] = useState({} as Document); + const [value, setValue] = React.useState(0); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + setIsLoading(true); + fetchInstance('/api/lookup', { query: { id: id as string } }) + .then(response => { + console.log(JSON.stringify(response)) + const doc = response.document; + setDocument(doc); + setIsLoading(false); + }) + .catch(error => { + console.log(error); + setIsLoading(false); + }); + + }, [id]); + + const handleChange = (_, newValue) => { + setValue(newValue); + }; + + + if (isLoading || !id || Object.keys(document).length === 0) { + return ( + + + Loading... + + ); + } + + return ( + + + + + + + + + + {document.original_title} + + {document.authors?.join('; ')} - {document.original_publication_year} + ISBN {document.isbn} + + {document.ratings_count} Ratings + + + + + +
    
    +              {JSON.stringify(document, null, 2)}
    +            
    +
    +
    +
    +
    + ); +} \ No newline at end of file diff --git a/client/src/pages/Details/styled.tsx b/client/src/pages/Details/styled.tsx new file mode 100644 index 0000000..da085b0 --- /dev/null +++ b/client/src/pages/Details/styled.tsx @@ -0,0 +1,86 @@ +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; + +export const DetailsMain = styled(Box)(() => ({ + paddingTop: '2em', + minHeight: '40em', +})); + +export const TabPanel = styled(Box)(() => ({ + width: '100%', + position: 'relative', + overflow: 'visible', + backgroundColor: '#fff', + padding: '1em', + boxSizing: 'border-box', + minHeight: 'auto', +})); + +export const TabPanelValue = styled(Box)(() => ({ + padding: 3, +})); + +export const CardBody = styled(Box)(() => ({ + height: 'inherit', + position: 'relative', + backgroundColor: '#fff', + border: '1px solid #ddd', + boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', + padding: '1em', + margin: '1em 0', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'flex-start', + width: '100%', + boxSizing: 'border-box', +})); + +export const ImageContainer = styled('img')(() => ({ + width: '10em', + height: 'auto', + marginBottom: '1em', +})); + +export const CardTitle = styled(Typography)(() => ({ + fontSize: '1.2em', + fontWeight: 'bold', + marginBottom: '0.5em', + textAlign: 'center', +})); + +export const CardText = styled(Typography)(() => ({ + fontSize: '1em', + marginBottom: '0.5em', + textAlign: 'center', +})); + +export const BoxHeader = styled(Box)(() => ({ + borderBottom: '1px solid var(--divider-color)', + padding: '1em', + backgroundColor: '#f5f5f5', +})); + +export const BoxContent = styled(Box)(() => ({ + padding: '1.5em', + backgroundColor: '#ffffff', + border: '1px solid #ddd', + boxShadow: '0 2px 4px rgba(0, 0, 0, 0.1)', +})); + +export const DetailsBoxParent = styled(Box)(() => ({ + width: '100%', + paddingTop: '25px', + paddingLeft: '150px', + paddingRight: '150px', +})); + +export const DetailsTabBoxHeader = styled(Box)(() => ({ + borderBottom: '1px solid', + borderColor: 'var(--divider-color)', +})); + +export const DetailsCustomTabPanelJsonDiv = styled(Box)(() => ({ + textAlign: 'left', +})); diff --git a/client/src/pages/Home/Home.css b/client/src/pages/Home/Home.css deleted file mode 100644 index 5e5d2d4..0000000 --- a/client/src/pages/Home/Home.css +++ /dev/null @@ -1,26 +0,0 @@ -.home-search { - margin: 5em auto; - display: flex; - flex-direction: column; - align-items: center; /* Center child elements horizontally */ -} - -.center-container { - display: flex; - justify-content: center; - align-items: center; - margin-top: 5em; -} - -.logo { - height: 12em; - width: auto; - display: block; - margin: auto auto 0; - object-fit: contain; /* Ensures the image maintains its aspect ratio */ - max-width: 100%; /* Prevents the image from exceeding the container's width */ -} - -.poweredby { - text-align: center; -} \ No newline at end of file diff --git a/client/src/pages/Home/Home.jsx b/client/src/pages/Home/Home.jsx deleted file mode 100644 index 62c8786..0000000 --- a/client/src/pages/Home/Home.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import React from "react"; -import { useNavigate } from "react-router-dom"; - -import SearchBar from '../../components/SearchBar/SearchBar'; - -import "./Home.css"; -import "../../pages/Search/Search.css"; -import logo from '../../images/cognitive_search.jpg'; - -export default function Home() { - const navigate = useNavigate(); - const navigateToSearchPage = (q) => { - if (!q || q === '') { - q = '*' - } - navigate('/search?q=' + q); - } - - return ( -
    -
    -
    - Cognitive Search -

    Powered by Azure AI Search

    - -
    -
    -
    - ); -}; diff --git a/client/src/pages/Home/Home.tsx b/client/src/pages/Home/Home.tsx new file mode 100644 index 0000000..e2f78f3 --- /dev/null +++ b/client/src/pages/Home/Home.tsx @@ -0,0 +1,72 @@ +import React, { useState, useEffect } from "react"; +import { useNavigate } from "react-router-dom"; +import Typography from '@mui/material/Typography'; + +import SearchBar from '../../components/SearchBar/SearchBar'; + +import { HomeSearchContainer, CenterContainer, LogoImage, HomeSearchBar, SearchControlsRow } from './styled'; +import { HomeMain } from '../../App/styled'; +import logo from '../../images/cognitive_search.jpg'; + +export default function Home(): React.ReactElement { + const navigate = useNavigate(); + const [imageLoaded, setImageLoaded] = useState(false); + + // Prefetch the search page component when the home page loads + useEffect(() => { + // Prefetch the Search page component + const prefetchSearch = () => { + const link = document.createElement('link'); + link.rel = 'prefetch'; + link.href = '/src/pages/Search/Search.tsx'; + link.as = 'script'; + document.head.appendChild(link); + }; + + // Short delay to prioritize critical resources first + const timer = setTimeout(() => { + prefetchSearch(); + }, 2000); + + return () => clearTimeout(timer); + }, []); + + const navigateToSearchPage = (q: string): void => { + if (!q || q === '') { + q = '*' + } + navigate('/search?q=' + q); + } + + const handleImageLoad = (): void => { + setImageLoaded(true); + }; + + return ( + + + + {/* Add loading="eager" to prioritize image loading and width/height for layout stability */} + + + {/* New row for poweredby and search bar that takes 80% width */} + + Powered by Azure AI Search + + + + + + + + ); +}; diff --git a/client/src/pages/Home/styled.tsx b/client/src/pages/Home/styled.tsx new file mode 100644 index 0000000..2261b55 --- /dev/null +++ b/client/src/pages/Home/styled.tsx @@ -0,0 +1,53 @@ +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; + +export const HomeSearchContainer = styled(Box)(() => ({ + margin: '5em auto', + display: 'flex', + flexDirection: 'column', + alignItems: 'center', +})); + +export const CenterContainer = styled(Box)(() => ({ + display: 'flex', + justifyContent: 'center', + alignItems: 'center', + marginTop: '5em', +})); + +export const LogoImage = styled('img')<{ isLoaded: boolean }>(({ isLoaded }) => ({ + height: '12em', + width: 'auto', + display: 'block', + margin: 'auto auto 0', + objectFit: 'contain', + maxWidth: '100%', + transition: 'opacity 0.3s ease-in-out', + ...(!isLoaded && { + opacity: 0.7, + filter: 'blur(2px)', + backgroundSize: '400% 400%', + }), + ...(isLoaded && { + opacity: 1, + filter: 'none', + }), +})); + +export const HomeSearchBar = styled(Box)(() => ({ + textAlign: 'center', + display: 'block', + margin: 'auto auto 0', + width: '100%', + maxWidth: '800px', // Set a max width for large screens + paddingRight: '20px', + paddingLeft: '20px', +})); + +export const SearchControlsRow = styled(Box)(() => ({ + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + width: '100%', + marginTop: '1em', +})); diff --git a/client/src/pages/Search/Search.css b/client/src/pages/Search/Search.css deleted file mode 100644 index 17fe932..0000000 --- a/client/src/pages/Search/Search.css +++ /dev/null @@ -1,56 +0,0 @@ -.main--search { - display: flex; - flex-direction: column; -} -.row { - display: flex; - flex-wrap: wrap; -} - -.search-bar-column { - flex: 1; - min-width: 300px; /* Adjust as needed */ -} - -.search-bar-results { - flex: 2; - min-width: 300px; -} - -@media (max-width: 768px) { - .row { - flex-direction: column; - } - -} - - -.sui-layout-header { - background-color: #0078d7; - color: #eee; -} -.sui-search-box__submit { - background: linear-gradient(rgb(60, 226, 102), rgb(34, 151, 57)); - letter-spacing: 0.1em; -} -.sui-search-box__submit:hover { - background: linear-gradient(rgb(34, 151, 57), rgb(60, 226, 102)); -} - -.pager-style { - margin-left: auto; - margin-right: auto; - max-width: fit-content; -} - -/* Adjust the width of the search-bar to make it shorter */ -.search-bar-column-container { - - width: 100%; /* Adjusted width to make it shorter */ - margin: 0 auto; /* Center the search bar */ - margin-top: 10px; -} -.search-results-container { - flex: 0 0 100%; /* Takes up 75% of the row */ - max-width: 100%; -} \ No newline at end of file diff --git a/client/src/pages/Search/Search.jsx b/client/src/pages/Search/Search.jsx deleted file mode 100644 index 5b7dabd..0000000 --- a/client/src/pages/Search/Search.jsx +++ /dev/null @@ -1,121 +0,0 @@ -import React, { useEffect, useState, Suspense } from 'react'; -import fetchInstance from '../../url-fetch'; -import CircularProgress from '@mui/material/CircularProgress'; -import { useLocation, useNavigate } from "react-router-dom"; - -import Results from '../../components/Results/Results'; -import Pager from '../../components/Pager/Pager'; -import Facets from '../../components/Facets/Facets'; -import SearchBar from '../../components/SearchBar/SearchBar'; - -import "./Search.css"; - -export default function Search() { - - let location = useLocation(); - const navigate = useNavigate(); - - const [results, setResults] = useState([]); - const [resultCount, setResultCount] = useState(0); - const [currentPage, setCurrentPage] = useState(1); - const [q, setQ] = useState(new URLSearchParams(location.search).get('q') ?? "*"); - const [top] = useState(new URLSearchParams(location.search).get('top') ?? 8); - const [skip, setSkip] = useState(new URLSearchParams(location.search).get('skip') ?? 0); - const [filters, setFilters] = useState([]); - const [facets, setFacets] = useState({}); - const [isLoading, setIsLoading] = useState(true); - - let resultsPerPage = top; - - // Handle page changes in a controlled manner - function handlePageChange(newPage) { - setCurrentPage(newPage); - } - - // Calculate skip value and fetch results when relevant parameters change - useEffect(() => { - // Calculate skip based on current page - const calculatedSkip = (currentPage - 1) * top; - - // Only update if skip has actually changed - if (calculatedSkip !== skip) { - setSkip(calculatedSkip); - return; // Skip the fetch since skip will change and trigger another useEffect - } - - // Proceed with fetch - setIsLoading(true); - - const body = { - q: q, - top: top, - skip: skip, - filters: filters - }; - - - fetchInstance('/api/search', { body, method: 'POST' }) - .then(response => { - setResults(response.results); - setFacets(response.facets); - setResultCount(response.count); - setIsLoading(false); - }) - .catch(error => { - console.log(error); - setIsLoading(false); - }); - }, [q, top, skip, filters, currentPage]); - - // pushing the new search term to history when q is updated - // allows the back button to work as expected when coming back from the details page - useEffect(() => { - navigate('/search?q=' + q); - setCurrentPage(1); - setFilters([]); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [q]); - - - let postSearchHandler = (searchTerm) => { - setQ(searchTerm); - } - - - // filters should be applied across entire result set, - // not just within the current page - const updateFilterHandler = (newFilters) => { - - // Reset paging - setSkip(0); - setCurrentPage(1); - - // Set filters - setFilters(newFilters); - }; - - return ( -
    -
    -
    -
    - -
    - -
    -
    - {isLoading ? ( -
    - -
    - ) : ( -
    - - -
    - )} -
    -
    -
    - ); -} diff --git a/client/src/pages/Search/Search.tsx b/client/src/pages/Search/Search.tsx new file mode 100644 index 0000000..da33c1d --- /dev/null +++ b/client/src/pages/Search/Search.tsx @@ -0,0 +1,150 @@ +import React, { useEffect, useState} from 'react'; +import fetchInstance from '../../url-fetch'; +import CircularProgress from '@mui/material/CircularProgress'; +import { useLocation, useNavigate } from "react-router-dom"; +import Grid from '@mui/material/Grid'; +import Box from '@mui/material/Box'; +import Container from '@mui/material/Container'; +import { Facet } from '../../types/models'; +import { SearchResponse, SearchResultDocument } from '../../types/api'; + +import Results from '../../components/Results/Results'; +import Pager from '../../components/Pager'; +import Facets from '../../components/Facets/Facets'; +import SearchBar from '../../components/SearchBar/SearchBar'; + +import { + SearchMain, + SearchBarColumn, + SearchBarResults, + SearchBarColumnContainer, + SearchResultsContainer, + PagerStyle +} from './styled'; + +export default function Search(): React.ReactElement { + + let location = useLocation(); + const navigate = useNavigate(); + + const [results, setResults] = useState([]); + + const [q, setQ] = useState(new URLSearchParams(location.search).get('q') ?? "*"); + + const [resultCount, setResultCount] = useState(0); + const [currentPage, setCurrentPage] = useState(1); + const [top] = useState(Number(new URLSearchParams(location.search).get('top')) || 8); + const [skip, setSkip] = useState(Number(new URLSearchParams(location.search).get('skip')) || 0); + const [filters, setFilters] = useState([]); + const [facets, setFacets] = useState>({}); + const [isLoading, setIsLoading] = useState(true); + + let resultsPerPage = top; + + // Handle page changes in a controlled manner + function handlePageChange(newPage: number): void { + setCurrentPage(newPage); + } + + // Calculate skip value and fetch results when relevant parameters change + useEffect(() => { + // Calculate skip based on current page + const calculatedSkip = (currentPage - 1) * top; + + // Only update if skip has actually changed + if (calculatedSkip !== skip) { + setSkip(calculatedSkip); + return; // Skip the fetch since skip will change and trigger another useEffect + } + + // Proceed with fetch + setIsLoading(true); + + const body = { + q: q, + top: top, + skip: skip, + filters: filters + }; + + + fetchInstance('/api/search', { body, method: 'POST' }) + .then(apiResponse => { + // Map API response to our new interface + const response: SearchResponse = { + count: apiResponse.count || 0, + facets: apiResponse.facets || {}, + // Map existing results or documents to our new searchResults property + searchResults: (apiResponse.results || apiResponse.documents || []) as SearchResultDocument[], + skip: apiResponse.skip, + top: apiResponse.top + }; + + setResults(response.searchResults); + setFacets(response.facets); + setResultCount(response.count); + setIsLoading(false); + }) + .catch(error => { + console.error('Search error:', error); + setIsLoading(false); + }); + }, [q, top, skip, filters, currentPage]); + + // pushing the new search term to history when q is updated + // allows the back button to work as expected when coming back from the details page + useEffect(() => { + navigate('/search?q=' + q); + setCurrentPage(1); + setFilters([]); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [q]); + + + let postSearchHandler = (searchTerm: string): void => { + setQ(searchTerm); + } + + + // filters should be applied across entire result set, + // not just within the current page + const updateFilterHandler = (newFilters: string[]): void => { + + // Reset paging + setSkip(0); + setCurrentPage(1); + + // Set filters + setFilters(newFilters); + }; + + return ( + + {/* Added horizontal padding and top margin */} + + + + + + + + {isLoading ? ( + + + + ) : ( + + + + + + + )} + + + + ); +} diff --git a/client/src/pages/Search/styled.tsx b/client/src/pages/Search/styled.tsx new file mode 100644 index 0000000..f7a2109 --- /dev/null +++ b/client/src/pages/Search/styled.tsx @@ -0,0 +1,42 @@ +import { styled } from '@mui/material/styles'; +import Box from '@mui/material/Box'; + +export const SearchMain = styled(Box)(() => ({ + display: 'flex', + flexDirection: 'column', +})); + +export const Row = styled(Box)(() => ({ + display: 'flex', + flexWrap: 'wrap', + '@media (max-width: 768px)': { + flexDirection: 'column', + }, +})); + +export const SearchBarColumn = styled(Box)(() => ({ + flex: 1, + minWidth: '300px', +})); + +export const SearchBarResults = styled(Box)(() => ({ + flex: 2, + minWidth: '300px', +})); + +export const PagerStyle = styled(Box)(() => ({ + marginLeft: 'auto', + marginRight: 'auto', + maxWidth: 'fit-content', +})); + +export const SearchBarColumnContainer = styled(Box)(() => ({ + width: '100%', + margin: '0 auto', + marginTop: '10px', +})); + +export const SearchResultsContainer = styled(Box)(() => ({ + flex: '0 0 100%', + maxWidth: '100%', +})); diff --git a/client/src/theme.ts b/client/src/theme.ts new file mode 100644 index 0000000..2f69549 --- /dev/null +++ b/client/src/theme.ts @@ -0,0 +1,156 @@ +import { createTheme } from '@mui/material/styles'; +import { debugStyles } from './debug'; // Import debug styles + +// Create a theme instance +const theme = createTheme({ + typography: { + fontFamily: 'system-ui, Avenir, Helvetica, Arial, sans-serif', + fontSize: 14, + fontWeightLight: 300, + fontWeightRegular: 400, + fontWeightMedium: 500, + fontWeightBold: 700, + }, + palette: { + primary: { + main: '#646cff', // From your link color + dark: '#535bf2', // From your link hover color + light: '#747bff', // From your light theme link hover + }, + secondary: { + main: '#0078d4', // Azure blue + }, + background: { + default: '#ffffff', + paper: '#ffffff', + }, + text: { + primary: '#213547', // From your light theme color + secondary: '#6e6e6e', + }, + action: { + hover: 'rgba(100, 108, 255, 0.08)', // Light blue hover effect + }, + }, + components: { + MuiCssBaseline: { + styleOverrides: { + html: { + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + textRendering: 'optimizeLegibility', + }, + body: { + fontFamily: 'system-ui, Avenir, Helvetica, Arial, sans-serif', + lineHeight: 1.5, + margin: 0, + padding: 0, + minWidth: '320px', + }, + h1: { + fontSize: '3.2em', + lineHeight: 1.1, + }, + a: { + fontWeight: 500, + color: '#646cff', + textDecoration: 'inherit', + '&:hover': { + color: '#747bff', + }, + }, + button: { + borderRadius: '8px', + border: '1px solid transparent', + padding: '0.6em 1.2em', + fontSize: '1em', + fontWeight: 500, + fontFamily: 'inherit', + backgroundColor: '#f9f9f9', + cursor: 'pointer', + transition: 'border-color 0.25s', + '&:hover': { + borderColor: '#646cff', + }, + '&:focus, &:focus-visible': { + outline: '4px auto -webkit-focus-ring-color', + }, + }, + }, + }, + }, +}); + +// Define global styles that can be used with MUI's GlobalStyles component +export const globalStyles = { + ':root': { + // Base font styles + fontFamily: 'system-ui, Avenir, Helvetica, Arial, sans-serif', + lineHeight: 1.5, + fontWeight: 400, + + // Text rendering optimizations + fontSynthesis: 'none', + textRendering: 'optimizeLegibility', + WebkitFontSmoothing: 'antialiased', + MozOsxFontSmoothing: 'grayscale', + + // Light theme colors + color: '#213547', + backgroundColor: '#ffffff', + }, + + // Debug styles imported from debug.js + // To enable/disable, just change DEBUG_MODE in debug.js + ...debugStyles, + + // Link styles + 'a': { + fontWeight: 500, + color: '#646cff', + textDecoration: 'inherit', + }, + 'a:hover': { + color: '#747bff', + }, + + // Body styles + 'body': { + margin: 0, + padding: 0, + minWidth: '320px', + fontFamily: 'system-ui, Avenir, Helvetica, Arial, sans-serif', + }, + + // Heading styles + 'h1': { + fontSize: '3.2em', + lineHeight: 1.1, + }, + + // Button styles + 'button': { + borderRadius: '8px', + border: '1px solid transparent', + padding: '0.6em 1.2em', + fontSize: '1em', + fontWeight: 500, + fontFamily: 'inherit', + backgroundColor: '#f9f9f9', // Light theme button color + cursor: 'pointer', + transition: 'border-color 0.25s', + }, + 'button:hover': { + borderColor: '#646cff', + }, + 'button:focus, button:focus-visible': { + outline: '4px auto -webkit-focus-ring-color', + }, + + // Box sizing for all elements + '*, *::before, *::after': { + boxSizing: 'border-box', + }, +}; + +export default theme; diff --git a/client/src/types/api.ts b/client/src/types/api.ts new file mode 100644 index 0000000..6cfcb26 --- /dev/null +++ b/client/src/types/api.ts @@ -0,0 +1,41 @@ +// Types for API requests and responses + +import { Document, Facet } from './models'; + +export interface SearchResultDocument { + score: number; + document: Document; +} + +export interface SearchRequest { + q: string; + top?: number; + skip?: number; + facets?: string[]; + selectedFacets?: Record; +} + +export interface SuggestRequest { + q: string; + top?: number; + suggester?: string; +} + +export interface SearchResponse { + count: number; + facets: Record; + searchResults: SearchResultDocument[]; + skip?: number; + top?: number; +} + +export interface SuggestResponse { + suggestions: { + text: string; + [key: string]: any; + }[]; +} + +export interface LookupResponse { + document: Document; +} diff --git a/client/src/types/global.d.ts b/client/src/types/global.d.ts new file mode 100644 index 0000000..360c2d1 --- /dev/null +++ b/client/src/types/global.d.ts @@ -0,0 +1,44 @@ +// This file contains global type declarations for the project + +// Declare modules for image imports +declare module '*.jpg' { + const src: string; + export default src; +} + +declare module '*.jpeg' { + const src: string; + export default src; +} + +declare module '*.png' { + const src: string; + export default src; +} + +declare module '*.svg' { + import * as React from 'react'; + export const ReactComponent: React.FC>; + const src: string; + export default src; +} + +declare module '*.ico' { + const src: string; + export default src; +} + +// Environment variables +interface ImportMetaEnv { + readonly VITE_API_URL?: string; + readonly VITE_REACT_APP_BACKEND_URL?: string; + readonly DEV: boolean; + readonly MODE: string; + readonly SSR: boolean; + readonly BASE_URL: string; + // Add any other environment variables used in your app +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/client/src/types/index.ts b/client/src/types/index.ts new file mode 100644 index 0000000..80e320f --- /dev/null +++ b/client/src/types/index.ts @@ -0,0 +1,4 @@ +// Index file exporting all types +export * from './models'; +export * from './api'; +export * from './props'; diff --git a/client/src/types/models.ts b/client/src/types/models.ts new file mode 100644 index 0000000..fccf630 --- /dev/null +++ b/client/src/types/models.ts @@ -0,0 +1,44 @@ +// Type definitions for the application models + +export interface Document { + id: string; + authors?: string[]; + average_rating?: string | number; + best_book_id?: number; + books_count?: number; + goodreads_book_id?: number; + image_url?: string; + isbn?: string; + isbn13?: string; + language_code?: string; + original_publication_year?: string | number; + original_title?: string; + ratings_1?: number; + ratings_2?: number; + ratings_3?: number; + ratings_4?: number; + ratings_5?: number; + ratings_count?: number; + small_image_url?: string; + title?: string; + work_id?: number; + work_ratings_count?: number; + work_text_reviews_count?: number; + [key: string]: any; // Allow for additional dynamic properties +} + +export interface SearchResult { + document: Document; + score?: number; +} + +export interface FacetValue { + value: string; + count: number; + selected: boolean; +} + +export interface Facet { + fieldName: string; + values: FacetValue[]; +} diff --git a/client/src/types/props.ts b/client/src/types/props.ts new file mode 100644 index 0000000..3de8b75 --- /dev/null +++ b/client/src/types/props.ts @@ -0,0 +1,40 @@ +// Types for React component props +import { Document, Facet } from './models'; +import { SearchResultDocument } from './api'; + +export interface SearchBarProps { + postSearchHandler: (query: string) => void; + query?: string; + width?: string | number; +} + +export interface ResultsProps { + query?: string; + searchResultDocuments: SearchResultDocument[]; + count?: number; + skip?: number; + top?: number; +} + +export interface ResultProps { + document: Document; +} + +export interface PagerProps { + pageCount: number; + currentPage: number; + onPageChange: (page: number) => void; +} + +export interface FacetsProps { + facets?: Record; + onFacetValueSelection: (fieldName: string, value: string, selected: boolean) => void; + selectedFacets: Record; +} + +export interface CheckboxFacetProps { + fieldName: string; + values: Array<{value: string; count: number; selected: boolean}>; + onSelection: (fieldName: string, value: string, selected: boolean) => void; + selectedFacets: Record; +} diff --git a/client/src/url-fetch.js b/client/src/url-fetch.ts similarity index 64% rename from client/src/url-fetch.js rename to client/src/url-fetch.ts index eb09924..640abe8 100644 --- a/client/src/url-fetch.js +++ b/client/src/url-fetch.ts @@ -5,14 +5,21 @@ const baseURL = import.meta.env.DEV console.log(`baseURL = ${baseURL}`); console.log(`Environment: ${import.meta.env.MODE}`); -function buildQueryString(params) { +function buildQueryString(params: Record): string { return Object.keys(params) - .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key])}`) + .map(key => `${encodeURIComponent(key)}=${encodeURIComponent(params[key].toString())}`) .join('&'); } -async function fetchInstance(url, { query = {}, body = null, headers = {}, method = 'GET' } = {}) { - const queryString = buildQueryString(query); +interface FetchOptions { + query?: Record; + body?: any; + headers?: Record; + method?: string; +} + +async function fetchInstance(url: string, { query = {}, body = null, headers = {}, method = 'GET' }: FetchOptions = {}): Promise { + const queryString = buildQueryString(query as Record); // Handle empty baseURL for production (relative URLs) const fullUrl = baseURL ? `${baseURL}${url}${queryString ? `?${queryString}` : ''}` : `${url}${queryString ? `?${queryString}` : ''}`; @@ -26,7 +33,7 @@ async function fetchInstance(url, { query = {}, body = null, headers = {}, metho }); if (response.ok || (response.status >= 200 && response.status < 400)) { - return await response.json(); + return await response.json() as T; } else { throw new Error(`HTTP error! status: ${response.status}`); } diff --git a/client/tsconfig.json b/client/tsconfig.json new file mode 100644 index 0000000..ad52ac5 --- /dev/null +++ b/client/tsconfig.json @@ -0,0 +1,30 @@ +{ + "compilerOptions": { + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "resolveJsonModule": true, + "isolatedModules": true, + "noEmit": true, + "jsx": "react-jsx", + "outDir": "build", + "strict": true, + "noImplicitAny": false, + "noUnusedLocals": false, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "allowSyntheticDefaultImports": true, + "forceConsistentCasingInFileNames": true, + "esModuleInterop": true, + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } + }, + "include": ["src"], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/client/tsconfig.node.json b/client/tsconfig.node.json new file mode 100644 index 0000000..165a9ba --- /dev/null +++ b/client/tsconfig.node.json @@ -0,0 +1,10 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler", + "allowSyntheticDefaultImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/client/vite.config.js b/client/vite.config.js deleted file mode 100644 index 913f14b..0000000 --- a/client/vite.config.js +++ /dev/null @@ -1,10 +0,0 @@ -import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' - -// https://vite.dev/config/ -export default defineConfig({ - plugins: [react()], - build: { - outDir: 'build' - } -}) diff --git a/client/vite.config.ts b/client/vite.config.ts new file mode 100644 index 0000000..3e777ab --- /dev/null +++ b/client/vite.config.ts @@ -0,0 +1,23 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], + build: { + outDir: 'build', + sourcemap: true, + rollupOptions: { + output: { + manualChunks: { + 'vendor': ['react', 'react-dom', 'react-router-dom'], + 'mui': ['@mui/material', '@mui/icons-material', '@emotion/react', '@emotion/styled'] + } + } + } + }, + server: { + port: 3000, + host: true + } +})