From a5eb36a3f162de9c66939cab9bf05d24fe3f7cc8 Mon Sep 17 00:00:00 2001 From: Akhin Date: Sat, 30 May 2026 14:15:25 +0530 Subject: [PATCH 01/93] getting started - basic frontend setup --- .gitignore | 2 + frontend/.env.example | 5 + frontend/.gitignore | 25 + frontend/README.md | 16 + frontend/eslint.config.js | 21 + frontend/index.html | 13 + frontend/package-lock.json | 2424 ++++++++++++++++++++++++++++ frontend/package.json | 27 + frontend/public/favicon.png | Bin 0 -> 72054 bytes frontend/src/App.css | 7 + frontend/src/App.jsx | 17 + frontend/src/api/client.js | 27 + frontend/src/components/Navbar.css | 11 + frontend/src/components/Navbar.jsx | 13 + frontend/src/index.css | 16 + frontend/src/main.jsx | 10 + frontend/src/pages/Home.jsx | 12 + frontend/vite.config.js | 7 + 18 files changed, 2653 insertions(+) create mode 100644 .gitignore create mode 100644 frontend/.env.example create mode 100644 frontend/.gitignore create mode 100644 frontend/README.md create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/public/favicon.png create mode 100644 frontend/src/App.css create mode 100644 frontend/src/App.jsx create mode 100644 frontend/src/api/client.js create mode 100644 frontend/src/components/Navbar.css create mode 100644 frontend/src/components/Navbar.jsx create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.jsx create mode 100644 frontend/src/pages/Home.jsx create mode 100644 frontend/vite.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..98f4b898a --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.claude +PlanAndProgressFiles \ No newline at end of file diff --git a/frontend/.env.example b/frontend/.env.example new file mode 100644 index 000000000..389aba163 --- /dev/null +++ b/frontend/.env.example @@ -0,0 +1,5 @@ +# Copy this file to ".env" and adjust values for your local setup. +# Vite only exposes vars prefixed with VITE_ to the app (via import.meta.env). + +# Base URL of the backend API. +VITE_API_URL=http://localhost:8000/api diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 000000000..1cac5597e --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,25 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.env \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md new file mode 100644 index 000000000..a36934d87 --- /dev/null +++ b/frontend/README.md @@ -0,0 +1,16 @@ +# React + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react) uses [Oxc](https://oxc.rs) +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react-swc) uses [SWC](https://swc.rs/) + +## React Compiler + +The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see [this documentation](https://react.dev/learn/react-compiler/installation). + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend using TypeScript with type-aware lint rules enabled. Check out the [TS template](https://github.com/vitejs/vite/tree/main/packages/create-vite/template-react-ts) for information on how to integrate TypeScript and [`typescript-eslint`](https://typescript-eslint.io) in your project. diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 000000000..ea36dd3dc --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,21 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{js,jsx}'], + extends: [ + js.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + parserOptions: { ecmaFeatures: { jsx: true } }, + }, + }, +]) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 000000000..bcc4a417d --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + BookMyVenue + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 000000000..d5afc4a97 --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,2424 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "react": "^19.2.6", + "react-dom": "^19.2.6" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^10.3.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "vite": "^8.0.12" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz", + "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.7" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.2.tgz", + "integrity": "sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "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": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.132.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", + "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", + "integrity": "sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.2.tgz", + "integrity": "sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.2.tgz", + "integrity": "sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.2.tgz", + "integrity": "sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.2.tgz", + "integrity": "sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.2.tgz", + "integrity": "sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.2.tgz", + "integrity": "sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.2.tgz", + "integrity": "sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.2.tgz", + "integrity": "sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.2.tgz", + "integrity": "sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.2.tgz", + "integrity": "sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.2.tgz", + "integrity": "sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.2.tgz", + "integrity": "sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.2.tgz", + "integrity": "sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.2.tgz", + "integrity": "sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.2.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.15.tgz", + "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", + "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.32.tgz", + "integrity": "sha512-wbPvpyjJPC0zdfdKXxqEL3Ea+bOMD/87X4lftiJkkaBiuG6ALQy1SLmEd7BSmVCuwCQsBrCamgBoLyfFDD1EPg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.364", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.364.tgz", + "integrity": "sha512-G/dYE3+AYhyHwzTwg8UbnXf7zqMERYh7l2jJ3QujhFsH8agSYwtnGAR2aZ7f0AakIKJXd5En/Hre4igIUrdlYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.1.tgz", + "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.2", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "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", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.46", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.46.tgz", + "integrity": "sha512-GYVXHE2KnrzAfsAjl4uP++evGFCrAU1jta4ubEjIG7YWt/64Gqv66a30yKwWczVjA6j3bM4nBwH7Pk1JmDHaxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.15", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", + "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.12", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/rolldown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", + "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.132.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.2", + "@rolldown/binding-darwin-arm64": "1.0.2", + "@rolldown/binding-darwin-x64": "1.0.2", + "@rolldown/binding-freebsd-x64": "1.0.2", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.2", + "@rolldown/binding-linux-arm64-gnu": "1.0.2", + "@rolldown/binding-linux-arm64-musl": "1.0.2", + "@rolldown/binding-linux-ppc64-gnu": "1.0.2", + "@rolldown/binding-linux-s390x-gnu": "1.0.2", + "@rolldown/binding-linux-x64-gnu": "1.0.2", + "@rolldown/binding-linux-x64-musl": "1.0.2", + "@rolldown/binding-openharmony-arm64": "1.0.2", + "@rolldown/binding-wasm32-wasi": "1.0.2", + "@rolldown/binding-win32-arm64-msvc": "1.0.2", + "@rolldown/binding-win32-x64-msvc": "1.0.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "8.0.14", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", + "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.15", + "rolldown": "1.0.2", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 000000000..e4d03ce24 --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,27 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "react": "^19.2.6", + "react-dom": "^19.2.6" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^10.3.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "vite": "^8.0.12" + } +} diff --git a/frontend/public/favicon.png b/frontend/public/favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8243365fb8ca9fda2e062edb1ec8a22f5330bbd0 GIT binary patch literal 72054 zcmXt91yEaEn8gVYB)Cf`THM{;p+M2%?(Xic#fk(iEnc9-y|@;NySo%8$mZXj%}nOa z%e=|E_p|fKxp5k5@|ftP=x}gwn2HKAT5xdi;jpg@00s7oJoWfr*agW$N>LjC`vd|k zV`29sp0awLA6%?Geazgg;B1^-oUB+qEZnWEoIPw^JTDNt#b6Jy{r8ZxyOo)zor^QI zww;p|+y^sHFLz5bKWc6cYE{^Ui-U`gnuAk_ms^OFi&{f9$}=er4vrd5QASeRH~+L# zKZRUB>)^iqJ}V4Js0RLG?g$=y*4FvuMqF%}lmPq{+IcN)q$85qt&z&A zhJabyS1A?%hO$$@YlLc@gVr%zsOm+KE02`|=_9X;=MFWo>KF>@&Vl!zB2W+do$oidfE} zX&$Mz5&+viQA9j|nH-+$->FvsOIChjBJh3IK=`Pn6=4R(GJr=XET^0- zV~rwRe}y1~yfv0w4^9Up9fdxHN&w1t2d>?Tl`;TKu9n0Mj<&z7p+Z@31o7%fvr`%?6#piSPxsk6102fkDKlJ7!DQ`= z^6iJ6i7bE0()#!;ZX6?)pIJIRSPkBep}WdX1~nwQpX3$C`ZCY7g0Ul<-rn1 zCp{g5eD?GYTNkA+no;Nsgs{z+16M>Dsa6T1`_{LIdl-kgm91Gu0i5BZA3VEJvFEok z3=ivc7h8YzE#I>1Lm$|_P}SB?p!2$uGP?5!#z&Ii8Z_B^0J|2pQQIPna!UQ~fc9os z`!>ad(yZ_wP;n(_m1qvlSc*?8wczHh*gkXXe16l=Hd|{6(&_g_&TL_O*4-*4wfCeT zU{>MO{_A<}DK#MMml-gS3-P#Q=0x1#^50gNSvk5|^seLwCMY5{Kc!9%@gF_^RbP5B zdCgm&7ozai*d@l#0?MjM$%P{WB|Dye+Li%GFiSAT=0aTIU!MG|yWUV{-V@n&`RDo` zq4P|JeG5~pC*}%7Ne>qxs-Bnur2Qc-YO^2J1v>w2#nw95q+*++h~EH0UL2B0+eR*9 zq){cU2pyZ@=yp zf}gMu=Z6pRG--(7vt~=Ri#1ZEtI_K9&ZgWI*=}!DMvMhSYEfC7@=XRs z=ciT_ll*^T>jg^;?41Qj0ko+@32AV{SYqlZr$g@f1OFAhiqD5U)A+x@U*F`KAu2RT zs6^7xe^pPuWy`j;#cp|HMNg2cET%Z32F8+1_h`{Fn8n13k~S?L(DQ80;V3bkEd1zaGv&{s| zDX1r+6I39=BqtU75G(>IT}5%-HZdAm2ce2^Ds*rHLk zY_dyjKXTe8yxY5-Jkrunfs^XS<^dh3-ZQG&FNE#K{qTj-U% zK5@-_=5BgBy-!_Wn-=?}@^Xr6ZI{(y9OI1`2lQhj=1J43Tty|U-atBAlQu9@j#IJ% z3ygy+St8MAqvHW;G~vPkP!k4jDk-t4q@a`N2vUYseY51{)I^zerY9!sY3{G(@W58n6DxTyP8`rC*1&S(#YPczg>*u1?7d8rW_x(_P z7H&Xx?CTBJM3s%Lk-qiT%7SgTJEhkx!Z@FT4wjn?IRgZYqpH9n_ zRNAaXLJs}piixw7D5W!nY^*fnTa}qT1BxVch*YYGo-NK(LTQq8P)DHt_eaa_YH-W}9f)Bt((` zIT7!M;iRLlIFZ@q;3Ix(jf=YXt;NkF6{B=XE&WB25fkMhwT1$i1f!a9;6bF)$tCI; znUn!#Wx&w&ThjK072CYUk2Xt>m(YRi$$HmEgj)~gIX0H!6|bAX1aBVc2>KVJ9+_CJ zGK6nMNs#8Rv@^sfd50wMpC4K1SQ$l^u}Y<)G7@2`X2MSfRs-k<#A{4Flr2NguvWXK zj@8#=y1Nw~rhO*3T7avTJw5I9KHdrM)0u)1Y_|R4u z(HFY<(tpI&$H9{dScPC^`(>8a&mFDVyS;_H0VvoT|3z@;I3$dRT#(*Y+1MG2DxYlg zre7~qGDXz#E7HnV@U(^lqp%FC@pT;%Pb$NHvIRmC%wdRMfdF$C>jzKea)J^hx-4s4 zykgU@%HB}1fbO<27Q?2_O)@wsxp=%N)o#eN6;DTaiEP2Y5~FAtP-L(ILv#N{v{jhb zno5EyMQ|yjipAldH~@b747F8s?3ri5gwMytppOk#jbuCBfsuq4E zlWZB|QyEQp)72Ju{XG(dTro)QR>K{3CCCPOT2?YJIHQ8C%w11sK$BOBTM4$Pu75er zQ|HtxCp<^f|N7ccJsWOZiPSL<+DIHOIqq2SCn}|_{~Y?DQwVnyVaG`{6af%;fKHen zyYs;}Veq^xXvW9DVal6fs_F(=m0#W&LmL4_cg+|qosZ1db=~o;?jSa{QX=bbzsPXp zElIh`58ta+5adQHKtKlW+Lau1m9d-nQsLh*cIDC>X!_bh-M*%*pU-sv8hi=LGq0|g`-Av09`b5=19D9M)Bldbl@D+I* z;S9efXGSXwTqR*BL5#kTMI&FYv?}7~1j(K;S+UL_nEcQW0sSkk=am^?1d?V8J-tIh zs4riD52H5fo~;*I@HSsH<~gQE4-tIQ5JNEoXvTwIQ)zBur)6ZCIOo1KDyKL-onF#W zbd_`)l>S;SgKja$8!qx6-j#2Vul_cB4*G?% zurEYq%>cjKFY%)nGGl28BnFOcfK4fFONMRD?Pz$GipNNQ>9u3sJ<$W*6FKu8_%s`{ zzpu{`RABuFM3IB`y46H8;RFYH#f>B9@)z!JE+K>Ze3cCf$>u45E5A2R4cdEzmAw;z z$m%za32GdP={xC;9{-P;AfZBhJvO$lMCZ35);Xz!-{O8>^z=2NVGfQ4TZTXWOH`uX;yR?=o$7I$ z2IFS>1_z%B6Xuh0k>$}8gkc1LM`5lXMkKhBt{a&j z!C-v9*v#sEQ8Qza!qQzF?O@J&qJMGzc*VmpdZWf48*(YOKlH)hJ%f(As# zOQ+zAh=iWD2->!O?IHcyd=$4`l9%;ciJ;rcSIEnUH@-Wp9}5L0)sSRH;CZl$ z5|0zw!`WXI8QVkXMX^kx$^Q`>$4EZ5A?g@>eYiK?w(n!*YaNj$ef!5%_nZ)dP-g;8 zDXu~ecm@`t6BMF-mgSIL+{idk+#obBH5&I0!0v32SIMT&%Ax!|*J9@B_uUdx<diD`EYXew-f_yzEQYas{qf$&4x8z$!0rTi>l%(`om!TBQ}q3Z5w7N~88t7Ub~J>ShgE#{9P^OHd#9!ZMGZ zQH&*)fzCV`1P^*Q ztK{m~Q3yaPX!O?O4{xJa;hW<#00S1jF>+A#8JsAtJMM<7|Jy^(ku(lZ7}9EFRM!_H zjun|^dz>mW3}@rksAS;P`GD(7kTF-~cMe=feC^@MAIlyTx!XT6uV0`U^4Uxv2eH6?~l#i9^r@B`#Kx0!e`81 z$@r>8iwBtcJR~5J!6^=oIXKln&Z$jbVs$NdiPLHK3=@XylZIYdMfBf+!Li|CgV^t* zhM#gL5Oh<#AzJGGtb8%%aK9R@-K}i5uy6e{uv!~`qAL%TC0(o!ez+J5t;E0 zZ_R>Gp;oA}F|mFFHt?-}-TMj=jOw`I!KBb{V;SGZeGD*}s}tI?zcZ*6sgYKSi|~XZ zRN=SU$T8(2Y%xZys~T87_(bi*!~-he*8_br5F)2Vif^t`3CzpCks!a2`!fajP^CLN znH`prgcfb?Az2PriM#&h=&H3~eAiDH)oMpdA{0$tb`#>O`};kpNtb-8Gk}v=hv)7q z4j>BNUeqqqa}{u)#g%9vqlHc{p1`WVwFEN-v}grI4GcZHLO^`v#I`pOL1PB)@Mh}{ zabCPL!Rj*~(6t;@lA?xiL zrX;=I{l{-PT71Au|BpH8nR;Z$x@JO%r_>%^bPaMOvG!=K;YM)*4?9eZ#}c?t?v92@ zrdT6a;y?{z^f4yR{IbZ0GaYVwpHHUHJJq7wPvZk+i9>6MaFZfV5!8Oz|IbB=55Jp; zgpUDSC%`}EM7_QB+XIRUPO4aFd14ihlE&Z65kUFci74&z2Q=9+Zs^GXE$|;62GtMd zXiFTk6jY3;Ex!$$nPMcQh9*=|S z;$ks$$*Jq<#$*1xhC`fxXz4vsuCPz^VRMx!zGk&8Lws2U$v_?tp?TGGiO5JMqdq}R zPlW9Lq_TjxeGZ-WM~z!`yw3&iNGqyx1E+V!(UWUXg4<4K1TmB z<Ac%X#|xAlCWz%63cjnp#0D z)C#fS#?RSlM#ykLeRfZ=TenTi`!@W$Z4?uJq%*0Er;KE^0a9LBVO3eub75ShqPw`giZ-o50`rIou+-NGXPF2{O|3XR%;J92zMNJCrXg!~ML+ z{PUbQFH%hleV$B?LKJ}I5Nd%0VfsDyX7_q7aIs{P$e5yTK>bowSu{xW4kPr%($y&H zRT5z6@LL~V4xjIrZMo*CR943NL-@^0N?eHdm-028<#+FLCD&$m?|HhRYaymv!bFR{ zXXG|rFX+lHpJWS6!hh?Jx5`>iv9_EvXv&um509W4Klz~+CmkZ;!YbM)yb8Ed3H|J! zRY5{7bobqUu-GKaL4!{kp9l8^qm?mJZ(E_?nM&VJ#>NV>_(1t*{JWVKwBp$?Mfl;X zPGBA0@m%bpWlBw*u3c?)|1;3d$Rbr=MWj$$IBICjpZkOG!u0Wln8ksMcyVj{s0azS zt$C4Js`*lJO5l%)7j?1SnH1}9Z@u8gy8Lpl;IzT}Zs}{(jZ(hZ!SM+QDcUW1qBrKk$BK*7v`_O=!Qib#rK=^yz_$nKyE-@ej+?cA21JI)E~H* z72w#F{p0Ero;1em4eJk=x9HYASwi;0o$Zr_oK}C(WRc`w2N}&TKf|@rzDbb2=c;+u zi5r{89j-|sW6Xvx&LnM%D*P+zxjOatT22;;fs*SFKs8e;?!JMUW|U-qsuT$pH(m~f zQbK|d$LOQSH=LbGN`oThK=bZK&Z&wkS3J8ArK!@fQGLrWRjxJ|gs-Kz%yfApA3K4K zBYCAiG%LD(OUn=!z#6LS2-=fqhz)({{E^kcZ+g~vQoYgfo8>-Nl6#2F3qLOrUbt*` zSAs%zHY5CGeU7$#YP3$w{N*i36GYQh_4(;SAv$$ zU^65Jx$ZTl47wy~e`2}s=2qu3Ij9MNy2P>>m$<#MbOaOpRj3>yH4jsmNg&W=k_imI zNd>~)q$!xbqm55TE)GZVZ2K>qOSuZJ$i8%i{+I+G-W>)ukFurY$ZOKd9a7q(snBAf zKQcq)v*`sfanFAY?o3^{nR!gf=X%KSlpr-EOC)LGyH|L=8|nkFuY46{K4lnCjjm-I zrt7yB<>Omub-PBHsOGNzFw(Kf!%;1a=B))7@WEkW%F0CcH?k-c=3jRC<$bPp!vP7rL4?~P+J3{* zJ+F7(%9Ja44;GkVjPdH+q*~t!&dLGol30?o74=1^?ndYUh36UuGQWt6>$5XwKPJA? zuWIeolzANmOqpB=33zK%cRPYqzz7U;ZA~-dKZZ7+SoK;FBN{8SdXMQ_Q+jPB;$+|N zSmb`{XQ3!b#Sq`!Yd8z~L@bVlN)aTPM*16$=BJc-lmHpz=g&CjAjQ{}6uaPNc@?)n zRDmtf=PYBfEY3$~)O{ zD`ccU_?*FS$LF9?C5bDI8^)<;fkQ!q8}X;VPr`?+Wnpq+Z?(MZkc3rsUo#Ex^KCRY z)0CqIRwpCT3eW~BMFr?6(a2JdxodaJ=VO}gfv{iijPIORH@R4GDKi=p{$VZ_S`r9w zUPO4%hKpyuW|HwTq8D&dAIPRjN(@J7S)AAPuS)a6JvC`|ps%*S3ZFmkcDo-X0k0!2 zQkkTc!WnxN7-Rm#0;d!%4HB`C)6iQBG%w?Gna@Jc5edsK2_* z59;2+9>lx*MabqfiErv36>x6s9Vwd~G;u*6ZXDJI!Hg{5e6+k@Z1Tp4S%TjD5uvZ5 zeCuz>Cyac0kUQ}glKtBRmQU?6C!_k-p9E9%^JJ%ZhP?__d2mWcn!)|)4pBNN9|=Ik zEbq|GF?e0biTpD{nI^U=)n8bP>X?#AVEr0%noQ&s8OCXY(xm9l#G78yg1Y%>V5M2$ zmm?R058?_b=*(ARmxJfxx-o0hDD|H3#cGLGQJ?R{Dqf~OSsfkwd3vDr3c)+LZO>d7 z9MGEquh7i zGA2#yX?nR6;wGAp=rf~UCpw{v+Z~v{D(cUZH}s+Z<&}y9sQ1PID-mQk8k~;AUY3+fFi0*kZEL;gzJ$F;J zjoz6)xBw2*5wPsAc5X5lGxDxQ_qD5=+812(k5<&++!Bnj|5 zIz4T;ojec;X4tt3dCI(_>KYv1qsuaBAMbO>wlx-0_Pm-ls6P0AE&y=E5yOC$qYLAc z7HtZymc?+QWM&H4192F2@M%>YUhlQ=ER{?xtXSUEA&PR}4~`{>y%1>Y`n`WE?CCd7 zE-32V;901&!*rrBWhgF!j)lvh9?f>9+)CKvpQfX&E5kU+l`?H{eekPggW{;X(#w108i_kIG?qmr9wq*v#l_; z_E4#Qqe(;>Set!K>3m83>?3Bm^K~I^u_6j`_;f+VhC8;$yZ1R~{mJ<}N{JFqowRoOY#Nw4~t+v%GY z=$zGIUGElq0$^k+|14z1{0=`(g`WnNbXwtvkRWYDBK%2w9V9$@@q#3vXx$BuOv`s6 z^*M2j6#p}KYf8P?VstN5Dm!whXJtZ>mf4ONWWofF+43#zG={H)oeIYatPR8+L>hgij{kHbxIx z+Y_{Y+hyGkz7(x}xh&EOxi!owC;7q6it?Shs4s@UQz`@()&Xo-6qhs3B%Mr>9cXqt zJ?-lh7V*6o{c(bwL-$ot(`PU5-@Wr}WSRx;3zv&@*dK&A;@*u_%l;3Y{ zo=rnFgRb<0HKA9c(~K-66h#1oaWUO~m!8m}e(QxQ1NiGC8|%}seZq}AqH|Y3 z%w#sUz%{Ci#i#Cp@{rddnMS97A`D~51g&|IW{GgT<|C@_VKSJx{P?w7qlQ_~&oOzF zYjLT}=fs0A1j>Z=rAd=#-ts-N#$PR_4d?GUiw_W3UuM}f zV#`>G`E+TD8T=1q@>2Kq`Lp_Z=?K=?yLSK*H3eWob2MW-GKSRq@ZZ00_cX-VqYkm> zdn8w=aCOfVkfgW`iovHiwj#8*_@)iO9Gf0+e10UGsL=cOBF{eU)xmBUw|6@Gb!;T+2m2jQ}} zOL(8RF>%+oj15!20TIP-qOF5oIHLjzwqub8r8c|SjZveu#o&4Ba`nDn=(rquV0Ru8 z(gd0zYH6-;Qgl@{l@xBqpNpmk2SHEo9UP4UK0n`ovbk$_>Dx<)oop!4&@FBH*r*Qw25y@ab@I+~___$wdJpeI)8Jqy_QDb`(? z_Xc=PA##!GzU*KIDS;timKUG1ck4Bgpnt^(a!u^NOxxfYFQ#U}G}}yq`mAXWDa;SM z%C(t9)V4ebcG(p_=8*Fcduv6g-yVlnU$49OXV}H4%~A+4trZofC8U%x@&Sk2q{@k4 z@`-Syc>8U|R@n@bl#HH3pHXk=t;r9sK?{p)*B4{;t`li`DyMbliIHLll`}GxjYD?_ zh+WUu-DNN8E^b!S7R8kPxpbL)wHg{#A0!cAy^FY*&20plIeI-}%QyUAV)N#CKL!pOqc=2(2WQv3-I2|QTp9AD=uua|&ndu5E2%|G&fL5sLAt9<#gh6(9lF_p2BBESTkDMPu_Unr5H{(}6A0NVx$RJ#2#Si-L#uKd$iD7_#Jx z%Fh)>SmVX^FpFOVJpI?~i+P`c?cs|IN^rvyfn@*q)iybAb8k0Y9!1mJmiC#L2SwIf zqSATQj*G%|xtJEw2VX9(=V3UM79qRpysIv@V;25te9O7ei?R{=C|~`OjpUD*-bK&l zWIJc%(e&;P9uav^b)&D26VO zK*@^b#TOKmQd=`&!o*jlmVMkOlYCaTJF%hyWzrqK0{XWC&TPNNPN{s-5|p5rcUG#C zoTW1lEg6LChZ_?$N!gqU!N#Ys8%7pd}vZhnK^1qQPg?}m`}ShWdk=%+Tl26@r7W@ z%chmH;nw{Hjd!`s?(D}A=O(paRJ)n@Y2$Uvyx^rA+oI_E~|D zsJn|_TaIL4ZmEu1z3bM~V`Uh@=tJnZH3U;PQe?vRh*(RxWA=CT8Dyy>tSa4W&tjX3 z!#+}+^)ALxUOeLE5v*?Ti6o{Kt8hYzMAe67o_-yki*0q3W`a@!)u2VKT$QIv#MJ5n z7IUF71KnK{D{uYSetSi4Xgr^y$|Nm~I6frE#r?A@v$yQbxO3Yz<`3{B{(uuNgzRdp z!JCyH8L9DWnylx*r}eEgWV7^br|#$qxvrr{#EZpY*O6O&BBM|;2YLC_Y-tzNcq6Ca z(OoMRT*#VH7p5prFgO+TNF;kuJaerPc{`@q5*0AKhiP>)%@c@VFC#a>&W=LL_%v|i zJ3N>oW_>boF&%t{*+Px;C5=_k5xk}dlLP~~QqSE7`=p#OEg*^@E{Si)#GuJ@di&do zxNDSNaXhwB^C&yYD?Fw$#qMsBHg2q7t_?b+g@R42 z+H{tU3M(Y-MAEWL-!HKGt^K`&CDrO?!rS9p$pn2}gS^ZaArjC$^v}voOaME8A~G{G zmR>cpNL|FgR(nM2NBRAbd|{SivA{C)1Tgv%OYY8N>==jC;5%aVnptc2Ai#C=bY^Q5uW5#m(pU$lI`9!&1_m{FZ@fR_v4 z3{||y1{ndPTv`0DFp_~%#UBvn8hCsMLKCqJ%et4$tgI4VcT^{9&mrsu+5`jwuvsW){dZw8KvT8@C>?U9t&A&+yx}Ayxe2>sWOSz_cG_T z26grA66Id3Me=6dIO`!^P$*JCF%8ZUR1~bAsVTVits`T2X3cg-;HzBXAjYV3EkjYK zyO@UF`GcSvYxS5-c|A70F=x1oL=q%627f!VX!BF}k32Iw4pUG>&~&NK#e1PI?{lJ% ziQWq!!x|8g(gU~)z+50T0jUArR}x3(`p(x_c;U#xi{*px_7&l;t{AFSy--zZKoxa; zzQ-A4hwdhf066BUxD;I9KmXR+zqS#aEGj(8Ik0P&E-*4mcbN9#{_5s~4eYJ~1d^SK27o@6lj)7sUy*&j1^ z_!uT^MsKW-7b8^;z{(;gV#D?T3}&E@#x)P2m4D-wmM3a)SjG7+-KWpHnXvyfmur>6 z_T?8PSNqSTR!7AeiUzof8PZS~`${C!TPi5kI`zGQTxomI;!;I>bC}gs_M)XUmHY5V zH~+u&8|G1npve8ozg&v%1Wv~7N?=6jo1?TDsB6S&Kgz}1Kab@Q*Dw#4Pgak&RXlYp zvmLI{56>i(3kn9@ra;SiAKuR#*t1R%&4t;KwjN}BJ~TcT;?M2f7T1#$LO!~SuM?tk z%P1Z_I$+Y+smh`zmyA1w4rMxRvvwv&Xh`cDZK}@fcgV_Y*g%Wct z^}Al7ctq)x9KxV^Me(koyQI3D?mG%SfoiW7QRF zQ61%r9v6=UWOI=OR;owDv-D`%D2O48vbZpx%n4>WS=(xr%25puD;&AI+zYK01&;3& zy_X|#Y>SM|N6hM$Z+!~M$*akTCFiXbCqA)GpQ9i20O!j;I?Diix zkKk2d--{6*T#*$jYw+=ZGnklAY)XS;^WOc_ni{u@ zebu}9c{|B(4_${dxRiTL{RGL;n+4ue%g{yn3|LhrsflP#RX-lar}}hr=$_#GhLRGD z94~0kg41l>>f=Uu3~D9Rwxo8)oR&e`74!=UT|1_EFY4%%oln6v4|30nwS&YRX23b2 zG?8e0l~zkkuvEz-yS%Ln)l?E(G`sUW5nv%Zv&-JDUNc9C8pIXK+?N)XhZhVo7;xmK z?re`Y-tgad5@7nzk1P(Grr8eiSW2P31u-V8#Z&axm9X5CO!|X!wf7v)cxgL)1zF5d zd#UAS-w4XHXC1!AySZVUwlWyu1r6Xp%xC5j)=5UHN^!*|az;T?yOZK3X>rkAS8R)` zxg)|ky(bo)x+xQ^BG8?cDJDo#E8&>_^W}DO*G>FQwlU6}@x*tk#F7|z@qwX4Qq4Og z;&uP-8z}XwmH1aebXm5n0aE+i@ETG7g^NC-6je(z-}jK$9y^ zz;HhLFNcdY54V{7p`S@RUYB7twE&H%bP`Id&tmK89VJQh9I8W&i#tfJ`@QEdwUpVC z+%pbE?_VCI`%DMIEu(0`+VJ^3;qu|(u1pF;StnObUZctDki7R06+U$`T3Cer2K zoZ<80N-u^d5AT-+soAN`hbfD(^7m$Fk~eSbGiDsy9u&NI{n#N~Y{6mfrW&mq#mpU2 zq<*eYc>UY&1gOb5n??c76iWa~|5DS8?SG*Q=DoX5&|)4;3A_ra%p>lu9qR_nkQ+AY z1wE%*6cNEHn-L8JFwYJtGv4Altzpo(sTGEFyrk)dqyTF z^I-xOzpPsUM=tP=-DdR>;h~ZDQS-Hiv_UJ;hqTp4icimWG_ZxN-so1?7;_%o9Ba)( z-q?R@vEAT2Un4P#rmQC4Xzc$?DMd7T+4#tr^*)V+^@*8B3wgjGY`4C33K+Sj zU>Ums2xxYKA2m<5OvPVrmV?j#FIq2~Lia+M8x9rQk?xiEXk%zcs`$tw@_w{; zct+;Wr4P+E4ZbC^BW_q}8eby&(#JBQa{cW)l^PdJAM`k&1wHoxKM{IJ zj86k_M8a%FiJD@1MbGA74C?7Oj0@r8*BMpe>mfdk3z!xtO-Wt-XOr9uEVO>_H*AosfP$)z2@MHY-X|kZhJ0?T%|*N*CuKduaBjOCj$x}X zXOe6=_YqwJAuy6t(`@nV#vt40o?-#Rp-YZY;3``lBwp^Qb1t=$qaAX_U zbS#aV~)5U6SU_LA$Y)>G)fsNPJUWBC<=xsEd)fKa3djR7MW!I*&V+K`toGx(!csFrfh1! z3Omr0jBFIx^QPbNK8j86nL*6^R60%&5gxBugYSqmFwwH0mm$zr+iyDUz*=BFnWD_=bK=Xl{3^7}ts0rBpjptB)+6wGX zqu1jk{>xU~u_t96$>z}LjI~fvdq4uu+XJ|{oO^MA_cX_P%LFz$X2% zlpHWwTFJKO6+I<&0$=W@JxUK&kUx)}uSMA1o`116g!j&o(YP1}8hrA5g3J^W?7u}$>m?FTWa~)kSUum<5Gfb#{nm*2?@SImo|uz% zpom0Nni-{1J6wZq>OP4sR}-g9XhUl4#dOFUO4XZhf%1)l!theLTtOxlA{S4sdQh}- z`iFigdU;gTW}^_Hr1BN$mlE>nuNwjlli4z^(}&JNe|SmaFBa49&$kJ^xD3AZnjd1>Oy zrqD*iIbscldwBDcH|!VLHO0wL$HnADFT*)J19bAHg4vMwBBE*~n_|Jfl*d0a6-?i3 z9+VvT`4md^1_A>>0oEv!O~cx1s&tGL_!UR6Jr4nkyYnBqYxLYlH03Nfc7LV-pQg`d zTD$vdOhP1*N{Uo$d25uO<~5Ba37BEyOm2O^RNJ|G{B8Zi4{daTWwpMEr{Hn=K7=HP zgUSL!1Qup4Tc>E`m#^?c3_m?2KFRN1npUp&V z&RN5+gcdbFwB~@lmamQG76P>e#2E9%>>n6?9GKVN3sG4y-YMjEdwyP8s54)|bi}6- zl8P2JQb%P@*d&(NiXRz_Qf_JK4^0ze0)sBV=gpIK&DKUNfT|a;$ z#@)XdN}6mO^W)XS-=Rqa-nr4a{H>r+3Smo`U*8V0IkQ9@k=V5EjzwA&^ zJ(lIQGIEQuQ($^$(dLr;;_FV(JCm)kLn9D)A!S>z=mvj8<^vGpo6r;wfY{o8zN@aXHb`$wf*>WBM` z0RgPzR>6W2r#Ht9bk~k!q(RPBk|MmvEOIlrU}8*(WVy;&p@6L2K0!B{>6m*2mmJtHQ<>FVi z2lq(SHn_JTR1$zXWXO5)+<8`(kXzec*uL{i*n|gOh01q75Kqw7awHU^hLS`Nb-a%Q zlZz-}{iqUj8M2rWxSft=c-I0Ex0CfR&DwrPPUR+e4s-{F z=f}d1q|96jv+5ptWWX=H4s9^J+PL)p!(9uGkGGEv6-8q>Ad`WaIKCB^2BW=ZViu+S zFX@9V(ujaBwrvAE%F>wq_^YXmMBkam_&qeS+c$W5YJBJ04F+Guo*Mkq>D>O<^Qet_ zl>h`ivpCYEU7MOmDBF4B>Wm?NM2R&G- z-#KLswaQa0FwqKCIwR0AvN+Q;|D?i&Ke7T-+$o>)=S>o&ZGdft0O=8Kp*JykvSZkI41Y zu2{2-(Y*+hGmr6je|MC_G7#5!BRgzlNEU1EMV>5zDmfxsYfF~;+DVtoXa$m~|8uEg zjNGO+C}Hm-taqnY!IKu3f60%|irzrZFty+TYw!#aOENU;$NxkqfdBFfHVBU}WNgbf zgD*~)^+y0@Qi!7nnp*nN<;ig_O^FNIKHm&E`-k^4V6y4+SWCo9Wz2YjnwRz2x^Inb ze@faV^2PoEpj+T;Rx%R`67<`tp=}hv4c8Y;i)j{DN3HgK$}WJucYJ;lLnBee#b=uC zKN6A0Hqfnf#nC=8X(1glDFVj2G}ywhI-U(=Y(KCtSs%DZAVJew$BHBQPCk>mKhTkv zls!jFo3Pg{lmoYbnJXp`D>W9{F>3`~M`tpe3nLvpko)?Y4LwMwcp+tj4y43q&=Zd` zIvnlvZq6n|M7@lNEO1_>0*na4ij}2K#$iWK>%vk{_1^C?-_O3~TZJMaE786fx7Ni-@+i)^t*6Z#KphQWupLJ~f#B3iwUT=DQC z8|oVU^CMnx;6TAa#2vUTSRTRNi8sfN6bZjX3k)YVxL;Um@h6kb5J8ufahR$r z(*6?UO|l8`d)?(s>D>`+zkfCU({C(2MMYPp!X}+D96nxPD5It>#iq<|8||js0-B8H zV^_00gZ-^6*R;M#h5R+n{{YiKEWa3p1|=L=bZlte-(csuh+o0!f>>hN*F%SR{(jcNVeu&94M`5@KIzi+08vst3GQxKDcv19& z13~aKNnlM;Y{mj^;u+&Agtb5!V5hy59&bndFn8lkJg^Wj{1lkzVq&zf;R5vkhUWiATAf!1DLWUU7M7EvYF-HN?giH~#X^ zQI|esj4FyPq+@0~+`}h@rL&L{&#pzVHzr{E-UF_i0;HOzhsBfEu7hm1>+GQ3x|bPP zvEe!QfNMO6gg#WJ6qF-z-&*u~3t8r4I-x{sfmv#GuHeCEf1b$njO62152qc!y1)Hq z=HH{R=2UIiYu%-veLTk5VV#&^PXGO%kvq9aJXs?01c@=Iff|Yjp-Gsu-sdssd4i@m z5Cl(~jB`W?jX`zm%cOu-1MV*)@2^r~gAD87Fhwmp)OcjDQG(~ei=8s^-Yxy_d!0Ak zzRIaJ(A{g$yWi_ZvfC1b>mQJQlOAJG!?(h~1_rGymNxzD?d$g9xQD(jb?vz)KhBd- z_nAprc*G15FikXELXDScW&*Skm;|JtA7%OleaHub;3-lk2Z$wM5!*{pW&mi3_Ql7F==;<5KY#N zw?Lu@+;QW+W2IM{S`De)Mb9}7=;@Lec;DX!6xP|q<0K;&=27D%2m)(qnuDj;O9z7B z86aO-xM|!kU4f7?{>mdk9y+)y+WW@h_~>_3=L5{bqH^AxRV;x>jCQYpm~AmKoC3wWL*>j zr461I5G7Ep6K_>$-rFLf>4I{4fYm?08SaCNw(GXB_CU9bg6`<nB37MMTU9S!T%pe2&t^ zCH#eP%tVR&dXwn=TdaI|g|)Y?(qfG!RSNjzFi0XKamNaPehXmm_0_SN-tYAHob5d5 z?!JwAW>phiH8LuAj+GV;n1%4ZiDId=qcXadHEq0%Cpzk@h^l<4Fq?7nxI+Q-Y( z*dSn+JU$uHrX~@BrgKpB-ieJaq!kDU>;!ngNIZGN`~LI-)7g?*j7jIL0w2%y@vf6# zm?2nNf{`g8#ApFhSd}8p2;DOYpAF^cyQBj_@RUgifpGm%lSE=no1k82_p>W3|IK?0 zQDFi@nMPlr*lMZ?HVm5TtB+6mJ1a>`z3yGjI?dqe^}CDC_Xb1K0hREuH315ZA;J)m zAZQa)MNuK3icbrlnksYnzxorVUOCU9b0=YT9P$AK3OofR3Db+@&z|Gt#aC#3`Wfr* ze#ni#eVdibpOL4=5QZ#@fS5dz44RC)lO~|?u$2b%2;&!z%)ou`$MpN_v>*5QxHwUN z94w6_^^SYjIpmX&u*NpQg`*VDyaFThUa4PhVwBR+B@VxQl>E8lWPk7y-t06?j6%joXFOU0+EO5;L<)sZ z7AYqPvYN56EZ(t6Mn5~r-GBKmE891TS))k8lnW<&w2;Idi=bOfm9n7y$Fe^(*&E9I z{?RvW8}*N9YEO18f^N!y#HJr)Xb{t+jik)b;xys-7z94}poPE)5ZXSKuWw6#zvw^^ zJOg9^EkO|DB@tAsG*(w>wKgcz#@lCk^|O^n&tK^lX7y^`-@Pyx%x~(wpRT1~I{$2W zn8G+=uug*pAqj^lP^A1vCz-r>hKUPj;n)J4m?I87no`n26Dds_+pbVM$67w`lh2pQ zjfI4>Lrk8YXZYolWJkx@dh=5@e|LkJHFl`sWAMr06BDtGA$H72kXCj-ET=xzX1jso z1NQSIWEBSK>PIAo5JNzWM~x~q8Yt#jICq@Uxf#fYXwfEdl0$S1iG$|=2ZG?4Amh@S z6$#Yp=#^EPx9?-vBtr|oqlLQt2Y&x`b5eTUFkkDspHP4d>d3uh*Q=t9ph-fFhz7WL zfaER`#RRiIJI&m=6O5fXLFULJ%+Eq82lzge%MfM= zvUwK#A?A*q=Kc@AVEMP7QhoaxcUh+f6ljvcLlL3D*(cvzQC0U@?B{BFdd`Wb+oB~E zxX?RmQ`=Fvrx_!O@o4~640$oa__2AIDnqD!NQTI9+7 zzScF0WvOX1IXd}?lLiwdBx*ie_gKt1-o3a}jLo7T*u7C`) z`1uj!Jjh{T*uQ@I+oh*z zIl^GQKJ_%{+@*G7l`%5-RKIcKwRdhm*|Gc4Jgtvbbrw>WbkFOn&uDeN zkOZG5P({&V2tPYR>D6iE!Z?$^_(O6R&QdCmLy*Pz8H68#7oerYhdm)ii46cMZBWj= zZ;T-pl1L~LG{*PH<_&@L5QQwo*$IwJjZi$h$nvGnN&a>i@9s@P>cmtWn$g3AK&siN zT5NB)KP`Wg9iR8JNKZS-vVXpsp83{|neF}e% zAlNf+UmQ9R1kV5&G&%(_s8wmKtOL6g2|A56;G1y_>~$l3{Ui1=uWkUCvD~LQI_uF{5NhXBqm%NyaXmAa`n!Q5q1IEIRBHh4N9b-EfU%yp&>M$5)=vn*^xY6Az949O ze(-euCUiT^{&59?L6LM4JyT6QatJ}Te{2RxK#og7gsRgpr<1k((DwWCL;Un=-s6?Q&ux7u8sM!Q1!BZqkk|;np`z1*r(Rfm! zq>USdF(@Gjj2%^ptvc08A%{!Ip%N3LBZ#HrWHZBX^&acL{h0ONUEwavI-q1~GF{r=&CTn-4^!Bo!&(}SLF_xo?3VRq4C zyFx(pi3UML3y+WjMG~4+iIFTY`is}${L3&l2ENyEY$>Orn9gCu1HtCfa)KA3ckzKB zcm~Kb5q`1@t1HAGTtn@wPDccLFV%R~`W`q_c8Z)B=uP#ye;Tc9>KEddzMWn-uNsJ-!LNZ3- z8;O@l(8BRK0Yc(?K?h^F1+;`FQIgm?f)UaXpb4?Aq975jU{9ij0BO+1RxFSS?G0u# zgrze4vp<8CEyiDenf9;VX7@KA(SGj^+pM5jb2=$#6b9e9F5=Eub;F$!h3sK&-ErOj zxeku}!?MLvH%A&!P3^J$a<%sV{ewPz7d+9kRXxx z2qGZfhh02AeU)qGNscSfC}N~5*h$?Tk_fNk3AdFG4Pb%=sw7m1*~CyKB*Q%AAI)?4 zwUf;L_%(vVQ#R|PT)<>~NMf|nDB~d$ffNQK5{#1Fz@G%d0JMfE#?u;^2(+?&p|ubs zLV-54b&N{DHwq=4?2rQEOs`G^B>m$_L>8DRkbmts3zJ!Dr)Su`_!+xzU1t5GORTX* z0Y!-tIRYdRZJM3B2}M`C7d;)PyB_ZYf-aNZ5BaGpDD0;eF)<#39NCvg$WM+C4CTS= zT19EMYXR>0ra|F;rvIWa|6V!}1kV781oU=|?W?y~|Lq+Ts(4QSmmQb|D*_&Ml0Iy| z!Qk8DPW|*+29IX}q*H;VI-Xn6u?PY{6A>fvxXB6;WnzXf3~}h}af%lfD4#t_?#vRL zIt+yXGS-itBmymbi~wH?q;{ISgjm=rQLFJKH0w126oJ#aRjN7)z>~CKw5RRD`%amj&sA~ho68Y&<$f*+9*%8WzXIMKl%<8|*QezWM4S)g) zp8|$zPXSaq_9;{bq`-m?dWL%i_SYBTkHYkxrZ*(DUeX}{Af?VB=~4okM6^+8G87Jv zQZtcfUu9vx2y`F_o&|~%lC^C%zPw2*-XM=A1X|QFG@WVO-!h~( z|D=BcKU`Q0e)o72KRvb_I7?dLsSz|g*VE5zd?Etc*sP8!89t&yo*ZMDC8q!WIVN8{ zO=0m6%+J8Y5DaInrte9R3Z(@?3be3UAX+((kQ5yjs-6DWCMN59NNu3ehD1W84!d zeFlRS6}=#H2?;R?b#~by1PTR`aOLAr(LZ;UM7B$bfYLwK^Iv&~bX$ zy|OfcT_Sc!Sffss^Gv+{3WtCC3Zv&v5H8KZ=r9bIAXHR*2&C=S1_F_QG6o?bk%mO3 zT?^7sXt!y7DVM>I3~X1>_qQsc{ksTRle*OrJ)1Sl47%Tr@jz;S)b+!noF-Axp-^EQL z6wSUddsu?)!?MTrRx0lmEsrxV`g3%8kN4eCnu*rS$R*vjrKvh%5VT2Xgx~~g1&v zA0MT$w$9k`1;&re;UAuX@e<0Hv{eddt@@k>Y1OIG8f|Rgeh7)t0EVWKB!N$6VhjpH zWapF3;k=MuF~uCqy#m>8d!5J^aj5Yt(RsjbjU{RyX!!=HM4Hb4DY z^HtPakI`%8=ngCIZcUMeJo2QyC?H2+Zk*hqDJbS>kdVcEWeJ|?lLlR9(TfBFav%u4 z1KOj67z4(DHVCO8G88K@;YJ&UBufVEf~r0mALHN*`h&&2$%Eh2lzkK?V;7`_zY`rO zxTJG!DI-&WCd(EP8zihC7~&8|{`eIZ&!1%EKl_JJ%ApGxYJsBedoCwmpo9er73rxu zsD1XVi4 z=%EpU#c}FmAv;5``Kx6jnq+B{Lpax8tCJd>ir8MpKj;$N`^5HY`=%3`eqNbb%8b)i zMX3n1_ajM5@iN2&w24T_p(ci)oCjYLIf1PPj@kd-xD2AMFzC9CUL2D^Xsw^unetqa zflk4SFjfc%ts&N+4RJdnc>iNQ|DXO>cK^-046{KAg<%uz0FMLcFg&nYqfME!+gjM7 z8hBJjK5%^l+rR1!H(`GEorAgra zCmM8wCmrJ!0;2^g^og_~ZnUhOWDLqAsH6#T1KKTUw7L90|8utf`ctZxZ?ertj1w~k z%m5=yquC&$?QoU)(PmR2(WO*;bR9}*B&n-Ai-C8KMuYZ4uc$HXCwd;A9){`DEf}-4 zDg9P}b~Wra-FTG-H_;qg_$ep;yMIRbC$D41%GASJ>nx2j08qw8+6F^6x zn_xT;p%VZCv`B1Iny5`qCzOn`(5-jYNiJXI#(N*J^}EZAey~BF27)RcHA1QcR0uHm z?BXM+6H>#Y@z-^BJdJ7YkiBz+!)Jbgn3#vWLMn+T5)!FwQJOFDWk?{L#iKPkF(?6% zj!8(s^IR5^W!uUqAPb3M?mzx1lM9P%e|d$C4`zwpzK){GIyb3skGva$w8MdAfZe>Y zZccqF7JB@%-$%?)QD<{@Ui%_)5qsXBa%6JnaP|6cYw>peuKh0hsT)Rrs zCk%S`9TXDZXA9v#5PXMZw6T_GkBdVz+EkWT*txQTa&X7er4-`)-zjs1enIecjsGV# z0SA574Xhwo1uB4I3dtz>KRU_m8?UhR+G{X32Q#yf%|S-kpsayVTQ6CX(8&Ta284!K zfp9M^LECVyjRV9na&wpF{SDO0Cfo0RM&-jVx&D8@!!BQNo@sm zdOhX#^{C);&rkF-#f63Lyxv;`*L4?xpiV>$iBFmGi3QZm6y$OUY|n{D>qcpuc-@;T z)u+gr@2@a85Cq>MrSnf4+bmlcP};@@&>9*oHf}Dnb@e6*6^cj-1c0GIt&iEJVZYvz zqux;9<6hI(q5j+rXb{lAr%r|%0Zl@@(Q(Fqc$D(VMMhseOY!6qOiVzSg&`jlw4@bc zegw+7%Or-Lx_^v>Bnvt==vIV~F`?23p;1YUS82o574HA$9qzq*iF^OzJtS*nX>x)r z1!@>-#MH4kq=E!ZjB`7Y2C-qAm>SFYw~DOYTH&(~Hd%T73l=V(XZFHb!q?8@m2#A{ zPt%hmqD_n-NfNZy_`dJpDxBa#BSo5~C_uQtvyf2oU?>aufN*4paCn%ckYi}H0Po)7 z^Np(%5EN0AP_72SL$OQjy0WFg>-32}-ZOnr(D-4_qo>3C>=o^GUTV7isq4fTh8TN! zk;1WA$h!I)&qqlQ^B9AZ3WJAU(D&4lejo_GWzxp!_Qt~B8lBkqKomo>&Gzkk-2V6q zf^{-TEWEL8o*vV62c^r@epc>p@`!kzU!QDY8>H<5ty99bY1|;<3KaxpLS`ubc#+YI zCz(8Zobu_TaAX#WS༜``Aj7Dqg(ny3vI+u_%*qnc?)f5RstTlmvFp$uUVW&d8 zyG!NnGU9h%()!I6R^Gcp{pMXttdXO^Fadc4u#2IEppC)Mwz@klY$lTeGC&C{7#hTE zvrUl#E891y{lykHF5jYY?K;`dK4$)huizb?BQrh<#UjS@X&b0F+fMV0k;Wj6Mo9~T zfN{ZQq0qu=ow4h$n=KWsbu>K8=;1knAKan%_wI4)|NPhNynCOBdyJE?M3za41hi=r zfy-vImruX`d#~>%EF@S2Ej}$VLaO>qiq1FhueA4B?R(8z@$a#0c6^ zLLepTaRVeTARP#TZ-~;Ma3@dMbbYoOSFmJ^Wp-LaYz0Aeb(6}S4W>{8BnX6> zE6Dj-8h7q;=;d=1P8>%cS;8NmAPh42N@HSen+o-cZP6V+gCG&s9TrJ}78V;nkP3fn z1PVopv(psM*7?K3hq(RLd))rzJM6r5iJRP~L5mS^IHg_JN%!gQW!d`+hru$y{pmqA z!*@gd0FjK@Mi=arG9CfnnQn}S+`G0G&eep8QrCIVn`$G=B z{0fH_7LdxBH0hHN?x`r%xd`n9K_mr{a8(yV(((+x3h;s~USSx2JZ9+l0;MB!OdMTc zb$*JSfAJf3`3wz|vDVAOs0;14e~DS|JYmoKeizJlO{O!Odr9|QcM5E%UD9%lBukFm z)EHuB3^G0~ogig^rxa2gNP$P813~Z&P|A>FT#yh0+6Fvr8lme+tUTK(Nyo@Ig0*#m zPp(p6ogBt#mFa@j1F3?&OudVF-?awzLJerruD_yFmM{8VU1lNsw%gnqn}t10m52s0 zEj&Di@hIVyi+F!}mZd-Y6EZVXFf$FsJV z&uEme=ZJxtX@jtB;X@!024OTL38K{^*xG>`ci8&i5;y<)w`~0RB{Zvq>@rS~JRUI> zG%Y+75)Y3MjZYIr14$idqG;k{$PkAfnU*G`O_Kzl78wK?JS4Fb2oWVDK6#1=)@ZP5 zF0=8@W!B!wGqJHt?&e*FUphzO@H|Y6K&fDX?f_#fytFouxX_28D|}O{OFLJQHVJW} ziFHC&d5GBwCVuZCrO{zl=BK&;?kB9id7WLe!4RA7$w;yY0voO9VU!l$FL7!|1kRyk zQ+x*(e0p77#UY(l{SepRy@im@c6&u0hQQf&f(#8jcF{y+$^HH$!J%=;XRYH+N<87A zT%@jbY> zW$V2U@cz|%oI;Z&au$HYOQ%~AnF^XNrJ%)E=uSakd`42WJW8)V0TE+_#z;XV zpe7O!0%IUVB9jE_HIkiO%;oDW|M}l?_k&BU{qhPKb{R$EvxCnrJ8aVchJYag@)SEL zGHpV(2xw5EPKGum+J7*_(94SqotQ=4*kbwrdK+%;06UB#7)6qyjZce+WpG7a8DJO) zfhI6Qjn-fNzuf(+->`k|B;!AMjfo$>L3n%#IX^?0@-j>@0EMd;jNlLTY|pB5hJ z{Ijm8wzDmp7^;Bp?x7dTqIb^p3xjlz!HV{8R=e&;&q}SCUa~VdLEt<5Esqu+1wgV! zjW$^_zkd|(!~$fpBnE^~D5FqDxPIWK_oVjBeHcPrA<%b+=Edn)b|46zkc{YER09*v zblJ30l6GRyNn%qbck8t8Z<2MaN_8^FJ*TNBdzk&8_TGEO{d~frvqv`sXa>GdVIh3L zqeK(q>HuPPsk4O!CK>(7amufpWbC!GWRJ|@ADY6H3TRJagjIP{D_jYeA(N7{W08oM zSlDoBjHVzID%KQZ2%0TeS)+RMHfvX}vHQDE*!bsfqoXZ+Hpr0>It@6)qv$LE5L7X2 zQ9eN@O|A+&?(=4xeE2^XuIE;B(gh_BK^21jOW# zlo=z-4w`BwV4ugjW(=?aG+CqhafY=TRJZDkymXJT^QS2tUBZlwAidbOMl^!9)P*GXls#`brUJqnK?#<-M@2ft&l)wq;bA&7`O;kKX-}hC)Zhh^Ez$rF-4szYEJMNvhKdp)ZIy-hGB_Dh;K*4U&;M2tc@fo|)}(}F*ruCu3izG(Ief~Uy@ z`_&XpnlEIpQ@R{EMVrLsgykt7onmxh4zd|&M`-1D8qgomqtk&PctT2BTXe8uT%fp< z67Spq0JF>!BRvJSc>;eL)k6=NrOu0aYk4IyK4YiKA?P z_66e9$E<#MnVY})fC@1MAV)-r$oZ$OzdZ-!2(k6PFK?t-k9ra4M13t_`cu^oF2I)W$JRw;g*X?_1 z0p;u$T8d7_kVrtph9K~u-eU8UtJL0ErbI-DN4Kgn&0|TA-W#|ZO#j;(%(K_i1tAzT z`MW6iww+{^h=_nHL+p^lFwNLMeuafME|57r52xoaBLz&Zh%vU?K!T4)1|?jy!Wg76 z2!R7=1B5`jzU;~gM`8i37}Kspwa)gxd6U}5SJ?jI2KE1N8(5`Cg$Y`WA}Df?7EJ_2 z66YK;c0psLeo~C^M7-J`oFnQ`2EKI@D6iFaRlps>jmN>ghffC4^Boje4 z2=U8Bh7XN%^z;&wKfcJlle3tQuTgzxnN@BO(qs~tAV(2_rh!L7o`?(q69lxV6H}vg zd57roZFWvBVBWY+_=A&(GmEIj8O%f(KT*g8kP;&#NE_TmNS93Jg2~cWC`+YC!%avJ zhRVn=k3T-i#KI!^BS%RVKcVu$CGNlV8D6_dj+kL!94oX^LC~hZ8vQ{GF@=5>J)fT@ zr9c{1h-GM!p-P+l8C=Hb; z%+D}8KS%qE8{EHinH#_UfXZ*Ku|kz?8cYx%StlSu(!xWNBcSA(i|(>PmAkYqZ?S%P zjbM3$?ClNmCzi-89zq@-w_Vqij}V>}2$thxj3G%;gCsfCZ^=3!N&P+f)2g*YlJC+7RBDBS?dO;e?JSiOrf+wWT0x?~kT?Q3gDBT#;Utdt(xeG3Ztc zwNrzg8ud$GvhnE^uD$&}JAeK;<9df8btZ@yCM1U-OB<*HO>FtuFl{7T7`9P(lqt^7 zF#b<}NO0jO#pxMjc^GwQ8lCm2`ih1$XwN#7RQl2aPfJuHk%>fVFnAR+y5 z{_+;tA1#nOy}+^m;^%OD!fFO0fQ&`Q)JAq)cJ9HYN`=N{r71yV6cuf)tT3C!o1Y;( zG{)G8IkHD)SwHzP>(d`l`HRn3tKP&%k*9%21xc%KPVG0hQd=Nx`V#lVmt@ag{q*fJ zTPvg#F%iZIf;I|os*D;RB@QzveR_18OLXmqyP0!$-R1-()2Xh$qAS!!%&VyDcS<0@Le*Cz)PeB zQHum50xc!Go-+uN7^RIv<#dH()dt*IA^!3fl}lH-{pLF~{_ZwW>n=sM7$+o4n+%c+ z5l!x5fDn%wnoSZ~MARrzW0c_c4>SAPOH5xli#&S-CPyHbwP>H23_{qzD1dS0!~!}# zd4X+8pP)&EZF`B7NFmYEppC{TpJrC!O_cGAd8VePnL2-g>b2Wkf8!EsAAHK)fBzYI zD)>|>(4vH(fCgG*-1&=@bC%@c2E2Btv#&jO;1F*hrq%+$`%Sj2|HaDV6 zC}I{EC7_O^(TN?Tv8`_Dph38pRod+>>5DwcJ#7EmY~+##xHA-q3AxKU6%^yYf06N5 zUxkrzjF&}Ai4pcnjl(S2D*!~#)$T(tu<>vp2%eDoEl;-~MAsE#S+OS34M-wbS!V0f zr%0A5Qo(l_;5DNCj^6R~_iFZ{BNFV=8*<+%=ors7d|kre(*U+HRLE0j0>mUke|VJQ z=_Ablqd!KTJWPV5r6eX)knu2qha~88CmBRH@@oV!ur0t75DJe_fS-VF)`)6V)YUue z{O%)`-+Y&~|K}sxe90J^qhu%&qFE>7TFeV41Z%(!F`K|H5HdsQk54l7$|sTt#2K4aWfDUYgG}tR_oelH3nMX7*aAhY)}a9*1H^Cv zvQvaZMP{d`7@D8w{*f=Jygf|&o!e})LLLu=!lywN?K;YtPO5225sAGHdacT|CVP;T zKAH#q>riUd+OkBC3Jqd%l+PR^cl0>AI8354)@|ejfD!4+F+Hr`tr_!BQSidjfgpH7 zO7Vr#ETOcDG$rA*s(cW_c9rJx2Ce07aCN$NS$Tav+wQdes)Z$yXr`E<%?>d&+Ss6= zA&;g=6~%q(R0zm2$K)?gQ~tpzMot|koS#80F2HaJ4~ZviTtiD+5u0k%=`|%1@C-_6 zga<9D@e)JMShUO_ieY1m>aAPs-nvcwy^pB=?h`iO{2a|)hN)2|Pe4MQ3I)m}1jMeL z`xb@rfH4SZEbMpBJyRHu0%&h}=ta%)fD-$v4iEIe8d0I|&nGHWNil(x&X` zJqYcb0FGIv%s?wcgaO||X$%`pwjJX&Be=Il?b9!~_5R1)eeV;@?>=W}bB!4cWrmPQ zA~sy#H5j5y70otnwrNu#pu_@WKbocR`Z30SdI5iK&S}h}m>|y%ni__z7GzU~*irf2 zsHJ#6(sYNaGi;V=bbNT7U-djWVGzRo-VBV$=Km!CN5^vU(!S2(d?IXoTD`NpA5liAbn^af9Xe zzoc^e4oxt|Akmb7o*3+(lIBsvfTPe`4j7tx#j?|k@98u*EUT8sAGH8wuJMs$0F z90nWSiim0S{Axc+L%_KGMBnxbF+=1@7^MO%Q)8Q$1|f3p5YxZ-0rMBnGkW^Cg@hO_ z(Z~k0RDcI~!sh+kBleom4kszSHus;DHWuQw+OW0>_f~1#T4w8`%WQvig`H1s;(fVH zmb;8oqeKx!OiUdO1QZCE<1OwGQ>KNF&m5(n9AfeJUSaCB^A^H-tOS`11R;o!7>~eJ zMkp7Iw-|P&8(hWqEHKV0=zmsrC*khHkOoIh=e_-fw*F>iQ-C#2Ud9QDF$kH#^D}r0 zMS_t@IDd}OH_mhO=O456=7-#W^D=erk+4b;!#Ej+DdJPZP$z?+fT2i4gKJdY+$DPR zOO}7~K1(}4CbKk4W^n;k9!8Z)ctTRcJ|m$_U(q(kB`73@oCyew!P5p|1b(iF4BcGG zWtjb^e@gwvP4u~s5FdR`{nOj*UR|L|m4pU)GPI~5s8Jv!M}VSX<6S+jmO<9v-S%L1 z`>+($hq39@E!CvOIu)8MGJ5I|nM0F^@-R9KXrl2*aY-$diZVS~ zk!tt-BiaAtx|Ul%qe!u64BDZECfJT-T3e5GWF*E1e>i|*4mD9?_WWU{E}Ulb+}qsv z^?TfV?-CVm)1pa@CIu{Z(GZFfc}DT6l2GR!6|S@TrpM089maojhJ}k4D4sqAhv)Hz zN(>bX7$HgY084eZEewQ4;Tr=+TZ_T-(8|t36NH#Ehf#-ynOmA-^y~>Lm#%XE{mo)tabMJ7y&nwUrHa-noyWK@QLLBKtah z#;jAwF4~YLIj6@dBBogU)0Y|lz4MIz_#&J=1Eo>Wwo6))7))YNNCMBEHxf4A+7Ih3Oog+9r2jim<_|!e0SV)YtIY371&fs?78KlFw1MBnejDFuB z(VxqbRurVn%L=#@KD>Y3cSW;y$IuFl5y&KT<8NpYfzqhqA^dWYuslNU$Q;A3pJd{_ z4_N)>yVTzPgj=jp07igeBJxOb_!gCw8X0P25!}1{cbLl?M3+~YczqS`_z_Acj*(qj zM2r*>VHXI_hy<4hi_oT{d>H4-N>r+1K*KYv&?GyaXJYgy6Z7*-EFNWIc8)ufAJKU8 zL%4bw*$JI{ozduy?zbBY*q;Q70FA+;KyY@P(&8-M#0XkR8ahJDkZw{%YA;JV<=Y4J zm2@Boz7ev_K83bTi}P)~(b)b2+uJnnui&vu5km$G60EWQ&tB>`zfI?O-S4{ZT1f6A zsE{XnXqK@zj&bro{W+Xkz|4%$GBGla(ZXhi3#rHoWosP_#3ljHvcnYBG>sKVVJn7Z zl0a;rZD6y8{^)a7-}!*X-@QZloA(LXB}bJ!F+~(vVmz8O@DP+}lVydN`?P^kCVzg0 zH(oo*((&rRYw)uibKfKR_AAZH!zyA&| z|HECnQw!g|irVT^hDu56AxYtTI=fDAu2u!r^;kisK+7&u1QVs`theBm%dD=g^Y*2e z*gv<)&HqJ{fA)ZsbqWS#9F)pIHKEUzjz{k$&~?@rU0N?~Ahy<_-yz4Mwd)8cYeL{S zn(u*okx3Y1UJMZIIgt?tjixZBECV1&5L(kgAoCoyA2Rsszv9RL`oE$5!=Ljl`Uvil zQ7~l%%^qFq@I>7$ERj)3;DjF9w@!b=8h?%9U;L|o#kqg>4_srI)2Cp41)B=;+&W}k z`4l0coWoRr))s9t3X`K!iAX$a6|S^F2Z=B_91P*hTkQSydp!8F_Za+tUyu!MvB3^6 zaX^d2Yi+6TIHs_)xQF5fQ}!^da+O#Av)|>_fBakUgKxvB9-LdpCcf{MP-qfD=PB^~ z%vPGejONEBFSzVcYh68_GQ}>6>7Cb}MKq&sT;MrYgld>a}WNATHAcK_Eu<%>W1KRLx$TqGf5;sMN*d32nm5NxmN z4$Sj0G(U%^pAJ2?_=r2eJG}B={KxeE(|=0;wO5%WZE_NK8FDcgV^1py?a2LT?9FjiTkOn6}k9fs?z<# z83S2z6bUbpU%dbqPh-|rk-aXoI*gP+6a_7p>g@IuL92Cou2O<_D@FMDdOaLEyfu+v zVf^!tXx-o8(R&~8;7@;w`J2x<#XhGPutG+{5DgTx$=PItVxNp%#u#2_?VVS-{F|5A zc>5|F@4f-AUxW*rjBG|HZF?J}EZOHHJ={dvCd@yhz|FIisA0YHbi@F8#oJd9k<+lH zRTwNn3ReWqS|4ph6-5Lfsyb6rNJQGk%9KoNio8Im6no|@o!?pE^2=8_dwz@Izx@dh z-~W(%zxav~_c@J&jQUh1!8>Fa9w2zYl$%UH=y2n~Jsyp>dHLP%(0}_|P$Y0h_>7 zf=UD?_qe}Tg{{yaELvDHF(tE8l5tKtEohH&*dH<8-6MH)z>PotbM&Woxc4_7G3FYl z7_x@TV9rPw&|!>aj9|i)DG3FuOzE=qFTTf1|Lpf!`Tjd_@hqI~gUlH!gXpe$%|F)l z8z_CE`#oQUReB5QoA(jqSa#VVXM>G5&vN?G1(MZujFm2lFbbh^)nHx3SP7psXbA?* zr==ixM&uqVQDWM%0I;q@*RDzXiYX3aqWU@g|0ST`;lGMUj@R*sB?g zFHykplM*ue%2|x2D1O`cDJb_Y8L;#gMB6vLG5)>q!XUCl!lu?*bfM9OCP@<7ZC9by zYD1po2n5ObExP?Km(HDIQ^=3WBF4A6hAuD{^M7Kp4n}6I9xPokR48pR@nLZBhoL)T=-W zGzx_ve+*qV2m<#AblPoXyA4Vqg-dJ;+ZquVffod!tb%ZGRyxfP#=xBe_CC1A!}qT7 z#b5r6>wob9k^?r-tl+GhU2-NAY%>P-*#-9Lp}4^McdxSbd%wxqcfZZbx88u)uEN#^ zq>?Nz$c#Y?i)i=htHkF-#~BjAGIBHn-!w^xasaVFPQN?%ziDuo`fSFLfdSF4zOM@* z>m^4VIaNP$wY~6IbU1#Jq@*hKLv$LrkOHACg((;(0&#kSm9=$Fz498TzV`$8>Lz!7 z@d1bLT;s-n`&X=yld+HHkd)k)S(`FuK<3lD_Q-hf)33Py(=Ryx?vL1b>na!j@CS6? zxXh{ZTZl8KXtfhkUxqsevTz}DBy67NXl-caIg`nlEXzt?$%F#{)QK~6>t29aCFf{N zCf;5nnb76b@1Nt`_ufKWz5rIBbAuHEDJw{*+<)1y{;_E(2%Z&*z|24xS9$?!VdsDc zU)&(OyT>ZFbP868zw0b@tm5174TPrN0vPQdVh2M+mXjnBqb+DlqTB;_nO!L!Z`_ zGo)9}!)lLE%*&Xg@*C>U%aV?!r671l6m)c_A-eExjApvM%k_^w#c)UmTQ&))+C`cu z9%%8g>&v=|f|tPX{x3a>q8Ck@K69YS{ob4)m7T7E1W&I z#n$(}OPU+k9#SZVhlf~QU`&oJrs(m2R2mXx(TSvJCA8L8>7O}`TwBMsI^=1>R0*cW zVl1>|NAxgNF1*Bdzw;d?yL;F?!%RlV=@?~l5}hNZg?0;M%R!DJ z#iE!L1tY83>l*B4m+Wkx@p|HffUw>IJsKiOGfOhg;jo~)pVPl-T!aytE--<)Wf7mA zL|M3-4 zv{kDT{8!(H8#^sjHb7- zg6Vci5`i)qsWJ##fX%(<+Cl--afU&Wq#e4Q4&82tZaySamb8^ZBAr#Ejgv*;I*coi zE~G0qZ3S3kXcYwvC-88G$-`aP&;9DEU(-=@6mNALyRD#6LCDM~yzfkr0x3Bo1|*1Y zz0ArR--7isAQDIm{0!izu=Y8n6tn0U1%wc=#aPCC@D!y zXqo9UilqUa8!}^=^juq&j+A5+WH1M+I}|I{;zRZ;VNf#MSQtdmTqIj$^t-<^ zPUEjZQB7u2wQ8IZ1jIg=>Z~*4#R&rEuv7>sNs@#DOJQ8zNhcK)MS-w_Ucb#?q?u~4 zsUjCZyTc^4SgFxM`*V3;6gYw6GuH(LQgx9C-w?>H#ncKPEL5$rLV!pl!bprMutfpm z3E8dNJovXi;nt7e$FRdGjL#rA8XEGdPD|q?s@3dU?;|tiIV=f+78xTRVd#JN z+ibr3yRdnYycnT737Q0Ld>69%iI(&Pv=ju-f^5iC^^brxFqy#KK4SlnRSZ3kwKg4R zg5!n2yx)U<+onM973p6HcJFKC?L9VcJwU#EfsIRB(Cd`&1lG6&r^1kENTg(IMbhgE zT1L__l6H=(rJzCO1?}k=(gac;gP0l6+8whcUF^Al#9B<*s?5!V>i4j{HhH2T^#QYz zGRB*7E(9?e4DoVJa5)!n6`DySOpC}(jG&h3169d&as+W|D2OC=lh6kyj`g7 zYC+1BzG%@ZB$Mq!#3Vn$BG2pl68h~NMcvb+!B zx@+uNSP<@hO07wX3_kmsyMO+-Z2#y(#y|OtF54swuw+$kg3GX&d#!aLUI#Oqf+5^# zQ64q-_f4XFlhT~bt2^tgUOtcQv=PRjmGbda@pUb={->p-Ab3U;`VQd#7Tfz2H|{aG z^~n1N@U?{;JuX@~Z(D((OTkM_P(0x9lRfT!@-=(^=3~~re}$FTE@3wNJYou1gfZB> zKx#`O6iO)$dzNA9uZOD|rSMj1>r>u!EfO_Ad8+{t44N583MUQART@#!d(b zjT8onMi`AS1$r_iA5RASNF>OjC zzZ3#Z?e_6TXs~D$d-QRNcLx}=PY&b^nGO>+hOP#UK{3TJCifkv3@r>j3@O2pZM}<7 zpq=jT;VVOmqDzZ5DH6**hV45px}=Q@gw-fCDTV~`a2v)WM8D&^DC@G8kHz^5gk8j3 z8go*Xn~v{?o&6o$UyXyYvJ7(Vrb{Ib=8_hPak&-JZEKZtTnJwy$Yo-O+uJ;EC7#^> z!4pxrK!bC>1_N49g@s%r^PJwnAzZ(O{?TU?xVZg{Jq#I{hh(5dO6u!5f$~MMUBejd z1$B-lBY&7sU@*Smcf7V%{Hu%f_CiXB6<;(Rf6RiEf)+l!7ehg(EQ8|K!ff=>JYet0 zdr94A;rwPDGR9~O8ErW_yx78Rfu3|Gp|zk$Bc!kWnze(qfYq8|0@T>*o|J zilQx0h07|I*0~|kwqy-Vrli>z^=N`*N=iWjv>4N&phbs{?i_PzBpwtD{$2qOt zDfve&^wcoMGL;GH>KfhCYxFkOQN0dDB9Nt0;)Rbi7-2XxBc>t;tHBzw$;9{*k zF~&Z4NcQCel23Lpe|H127h8;iU(AJowN5YgT{>nw*T#c`lyn*isE^%E5W{HTO7F#S>MZn==$LYMiZ; zGC{Ha<|?gA=b)RSQ;A5N7cN)>E&_Qe3Z4bM(1PHoWj*tpFN820W1FNJBMqIwfb8SX z*?I3hBwy3VJ!~T=%S(h8oLnG?8PwWQ)%gn_P+C3A86zxbS^Y;>xbUC+d+>YThE7Uv zrBAE93L_0|>%G{DvI?k7+~*>L!6TPDaPJYk{{_Pbf5Oi70}PO2fIS2u*{F}DU`mD| z#q`^-+Qar*n0A7-5~V$J6;x$fyxvlZ2W-RIb!Gc$fsk3MM93sQm_UT{Hx zIJu4!Bf!mLfMp^rIT|(EWAEC>j6eE}9(P#BX)t5I;N#saN3X+UhF)rIBPtv?@h^ij z{KpsuYXYvU!*5*RKqTz9`&=m2P>(Y7s=@TZS_M`iNl`un$rLc|t+1LZ=$wLkBlMXb z#q|_P%aWFk))|JCjE+ zu?aTm!LVTG%R>eq?~)XTlqr+R6xm8~8RLSYWodU?u-SvH4yZ1S`X#qE7>p9mib*AT zQP4^f1fZvxtHoJpbe6kv7Qg*%_U_%}(a*o6b#K5KB$AAjtX$WIYORCxwt%6klG`-Z z>Qwqvs9ORe67un^1QkajSnx+rI~wuR^bF zv)+oOQSqV+g2yG>h#rYnT-%qxHM;`_cOEghcj!wAyG9{-V?{=OW_PT{Zzm-OTzKU$ zdY=$11cBb0aCmQ<)%_vu(`RT2ciTm$jZ~R~SiU@58P8|($)^vIJY<>Bo@&?!c<~@eP zf|i+*9S&*rI|w6OXy=(OTL%-`Qh?6jtcCSn$?2q&zO6u2wy(Z2A}TDSTMxq61U{A!w=r z=z)Ua*Lytt`Yt@!p|f$CG))|wktVKcs_FySPHR5!gWMTyCkZGur6`QYJ9?zA8n+Fx%2sxxw_C4(g}m0p(^8Yt6CalZpq%e z#^`77vHRn{=g}X%N6HQxWUOOYqm5)jrQZthaZ-WlIST>LeynfOJ|Z0+@bw?~+xNNum!D#p(q=-=A;QP; zBuFe>iqCghd$dh@_YN!XT3Wwz*-w~&P<_*%CDw$UP>)toVc7ocCd1F~ld(&S!_uPk zR}zg`M%sHN@ak72m@f<#PeyDlROhs%$eyAp=#a9(>c!Kf8*3iwYCa>Pam*u5xTF)& zQV^VwN{wq?fiw$=Hi*H5ov-h*d+ioe4p~8wy0UOHy4tf^qd^b^y*Gg}yv|t%83(r> zF#O~TTKB(0=jB(RQ#FGJXOn@JkdA*zlz?TvSQW+BJ%l z)aM#(yndQiw@awSB!xPHMNm1AflwIF5CnmsV?m8G^qpPo-EAKH@!v51H$P_j*=;u1 zVFN=S=cP!PBFUZRj=+-R+PFK&rA-gl18jnWKXUIs&?U1P{rFRE+?z7|`XN{TgMUEx zyWe8v;#thFSpijXW;50of z2V_2v+BwZdbezx>Q{r-?-P~9Tq|bn8qft!hvcVnpd3f`E?0+|*zqZPywRPmiI@Aw2c zA3!NY)zBHo^xes;by{lD8n`{A#gr6Sp-YbrnxQX!qP(}Ka^hB=keKm@#L4>g&fc|? zk+VKhas)#(1zpzPyg>KLMd){ZBa@2+m?f16rntb050Y%D?K2i3T3%nth4&k7F^tdZU<}hlFfeu^kNHwqm_->%|*0%-M0yOPO`g8e*YnveMFxr zZPpN23LJBK9*|}1BbfJnf^Y!9UXB>&$Xugp#rP|0psbo1=B0$!HD)|PXA>lvmJcSh z<#`e$31fx~$QaWm&vLKL;afqvPEx^SDGiZ8Hdq6(#b8OT%gvC35y{O54BorOt!sbB zw;6DbRbQ%jn+Yzfx=V)+>l|>uA#6>2Dj-zMEU5ZF2LbYsvz%jvQ`}<2*V`Yn^S2vp zUOLVC+6tVzh_p~xU1lP?d5g|iB5ZXRZXuz-AeE(1nh}R2>@i{B%dNR7atVl*)0{*4 zeA`6(lC5b{3IQ7PSX&3$i$h~v4WdCt2ZKbRu#DIt!!dz2!KVb2fLx_sST9J!H62Bd z2YrT;5(PsNI;U0;TkEjW_KxBcA81KWK`*o*h*61o)N}&6>{E+WF1g)eDT;zLQLw+w z!@vD&zWTGjV3jdjtYSI9kg*2jGkSYWajTRkyN>Yi#frQFQUom$deekelaN@2_WU?J zWWpyQ80(m?t7PeZ%O@k!l|EveF=7u1>~ohKO}`v@fhMQRCLkDL*}k{Kxyi&ajew&E z=@g$Ek1bp4XVhIqt~18d5gV$7IT&*DkN=cw|JDD+_>-@Bm4rTqeYO#vvAF^Wh9MKi zOc2qmSkKffCW;}d>OF^Q$ zxRT?3Zf90k$b>c)w)biICNm*-T+*S(q_%#lo(=Jv#ImVgBN4_FuV1 z|DPNnI~`Y=A~6^tk;0Z2Q}<0&cPNCxT0kj*7GYd2e>bq+0N1#`+DGyGUxQ!3dT7dX z@fr86#jQ)>*QWu6B}Mouq!C0ohr-T#Z!$FomfR9f8fb4e9NjMBXoj^PN4P<{&UJ>uYtyV%hoJs*$Z zwhFQ5ix=;4ruIq6P-kp{lRP#NG!$MX=4`RTVcDVPLpWYwBDMV*C^J$alGM+aojpfX zwJ+pk2?h#5**O+b%yI{l%|dZPY_^^nT3C{ zWd0)xMt&Z1rleRp6cjw*{tvH_U%w6773xD*c7?^3Yoro>&Jr+wfWo?Jt;z@zBS~;I zo`Q3~i8xwc^+(hkx&HTg0$|=9_8U~|!+L>fYWp_VN;tpu7E?3WA-VBW%uJwoNFExaWyqNN}xDL@(4K@&I>GqgPvxJ*u^lxsnV zgNF*8v$wm;!)rG%_a3r_VU3iY&nK27jJ)YD7QUX`Kd^}6rQZZ9z%yqLE(`1G);nJB zOmTb*ME5snk1jsOB|XKDj}644001BWNklnUOlBBFW`b~^JejY^Q6_@cLFyv^_LvK15j0f4XM)g=B8L2=w;E+Ll5W z9PS@5y0=5h0bMLD42B`0^g@gNFJ=65*|2xs))BQq53?9+>21F$uTZX z&OR2+3Vm+B_cfE<1DF;RSys+NI9O%r)WxbhzMnlSynjHF_=0u`^P!=@sN+be&~Ecc zvbOfBfWElnKqW|nASibySOrgK3k2a?7X)R0F(6^(%m(tp7HplO&;^CD2%)^Kw$$XG zH!TGL5UWmAx7wMX5I`19cb87-kEl;^d;lzQqM5O>DE%(aQ?5!Gz(RT~@JdAm}jmz1M;UYe>tOEE^rE*$Sn3rr?~dM4GEiDx!eYSQtp! zv=EFC+~zfQF~9f(4)#gbRw<<0uZiIkW-GcZ302vI$WmG)2;!!U&S&Lza;-k2Yx1|M zcCu<_L1>&1*vIve)1{{*)TbIz)s6z?(R)J=?cvRn34(CWSn-H0g1&zVBo7%O|KMfx z53az4(_jV0SXc5Ji7>_otX1M==GI1n_Sn~w=F$tFN8o5a;u>28VJ*5x*wQwF)_fMi-C=N3b7P}-8MJnIq9{~VYkR9bH{a>#m)WeE_`Tn-j7?&f)_FJjDjX6I5H zeS!zjLg+pw{cK7o^915yiSnC9nQ-bg@CEhxX`asg6)YOBJEwLnJGB=T!i9!9<-sxQ zTH;kh;E7Tqg#3zF6{xPO3Axp8Lv-K`z-#_tUmGp_ChL68V{Xtq!#WPM1iV8|G`68EK}3LUY9eM9$DS`tpHvI; zC${t0sCR< zMhV($LqgP&erHA&FzLE3Ucp0Uuq`E1%b#BK)y7?ZJ__<#B#fSx!j;;_cj2w@J{2+h z+dPVH$~?`V+IOKduZ?nA0@OAuykpDnsG%Iq)I%Io@E%lWc-*@hufr;1I%Pn{At|l5 zFR=c~Mbz0%=(Mnj^fsR@^BBu>E`0*ejh2ESq8h^yL4R_jB@|L%wWVEv7-mdv+~@Gx z9ol4cNC=&K=h5>L0w+0FrTHJTU!-x)ZW;pyRly)9;^N>(Z*AmHsmV1U-2hP>2eQq- z#K+P0=i5+oFDB|uQBTTSeBm{ydyXIoBXCD?D1In`E^Ji;$}<0cI=JtsAsly8Vj-ZY zk1)0uPEIlOl!4F2P)z8v`MsBET{r`4eXJ4`0<3bvK$f+ez8Q=9MW&@7h^VfWITbl* zGDw^uZ6Gxkh7C(G=7S>lGNibSd2#J-UwJ5Fw zo3(!UbMETW0sKs8DF`BpA=r>BfRDF#ze%JOw6ukT0fVpalHc58lZ1A;)PFg&2p(!; z;XxB^KHts9$-zjLvLis5#N?qOh+b9Pkno?aeFmj7yfmuLIOCG8(c%W_&z3^Oj_Xop zE2b;r*@?#$V{lXwP6jdSnrOz(+TeuOXk6`7SNS%aF66in?SnseDL|X}AQp+l))@Uf zJH|A=u{@1CF$jVQE<{x$nb4xR+I1x*R$UCyR2y{S25xbJ(i;3aJl-X;q$j7PAONiO zI;__&TuEUZ6B43)Y!aZG3&i0BZat#-{4SPl5)PvZ{of1)UM?En&GQXB4O#DKG+q!G z|8JJzg-im+#tcs>2&@HTFxE!LDr!v2u`4l$kyQZ3^SC1|`cBD~)m;#(I$sm!iRZ~i zLcqu>!`G0_gyY7g<}|qVXsZH!)>u#$>rFW0{WxWCapmbJ6#ny_hHBRn#$5Sig6lkZ zek7qq<&!%96QE0*F;ni5GvGWM-+Pz!H{OB%I)xBr0$G4$tuPoRkY))DJ#ShH0#7lR zA+7Z#65NApq+=`!NHYr$58%dq#3$RN?2~fnTOd9zJ>Bx2=L={GhCols5Uo$Gj>~G; zQV_%xilPP0{b>aOU~H6wU`kC^P{sN;Gj~tuu6+s;uC}ZYASK4D<$>ph#F(tmAykI?`zg>uy)55)LYEE>M7dB9(Y zE~>gWR4pnW*GnMEqpTq4kkLnSnYGv7rgiZZ==8BNK`NJP9m;MS|1RW}Ea~~tQV>Ms z7)yXbN(0ruS4a(HIXv8E|JH4?d)u^_(#BO~`DN2WC-I`~?eWi-EkkH)dTkA3uJHT1 zFqmoP`?zHB&0fNNI2s!AyOiU4USy$cvJ=mrl~l=x1Od_Xr{EwVMQKIgZJs7H)CA+I z>Oet2S4Xwc->|<7fpbh1&%;U|k_1#eZOzNs@~XpgIdhh!rSMGXg%<=tZAy3 zu~`QDd+gu1$=R)NFjLj;Yx3gJzTN;EXx<8gm9R~XTjNo3)e%)H9s&!x%@-xPawVLN{%glA~T zkTHGKnGM=+T%dpX9IUTkq@|FdaEiN|$_k{YdVwt2{qv<4UJxv%NE5*`98!1TdG zChtF>!-!QR5>xiOJ$k{$k6yEG%hN$iO~zN(5dGupf$YV$Nc_Y_(8iofn)A+E-_y?6 z^R=9`=|OpC_fe>>A{GXf zt{>V`hTaV}+AA(nQQd2Sa2@o@d+K73PNuNTqvJwjk6ZZ)OAAd(>&?^jUb_gV*Fjh^ zK^c53ttG+TcUdc@v=j!0=P?lPlEue66`gKkza^39eFLyb@M;*=E zXE8WuOD9AS40S;=Pq5AduNDj7$D_&ykqcg~WM9fEiawGy-3zB!edQvYS|ig1c})=b zm#N+_>G{*rB8XUJz%pyh(Yo(btC1#$!2o+_2gw0_1Z}Eze@E-|M?Id;ezjLRYE=0X zsM;xp)7GJ&Is>u`ErNM=O$Bs#P|a4`&KqC+x6GIov$Js&LjB7ii}CQjC{=s`RZc;D zW116DGVgNp`IgqK5Dxp=3ihu~Qo<_OdG6q?h<7yOF%}}qnNWMmh^1t1s4R4%Lhgy!BsvKI2_j31yg{WjxjgC zV)v)-bMR*$vBrcR1{l-xM;md}f^8b*p;N4*C+S$wJmS$uu(b%5%JKmb&4D#OR^7r$ zT4=^#SBi_CQV5qGB>?GLR6?1XVXSox-Ek#`Z4@VN2|fbr<0xH$Xv&6)L0})P)0#5; z(O*|>CgK@*Tx#O;A*|A~!g;~~s_3iGxy=ejgYirpR2{nyA~oayILzOzRe==lUb*)~ ze$ya?Epd;H*qW;>xPagvb;b4jQzsQJ%by{@Pa%j^kkjH318xDEZ@$CjKlmQ4moH*E zEzr=>66MR5i<%Jd;wsGDucuM?Ye7pv;K^nFI|gW>d>RG5nMj@^4-QCn4(KwaO+uFr znqjS($CIA_S*-GT07?Z9HL77Q0*gg}9{fiyDTI(S@J@`=JNV4{-T)OOLSvie>C5kJ zv|+s0jMXx;(Kk*OUGovdBMR4E-@fri$iSm%dTiops4St4ot3JE>mmVUN2}3{*;1=m!>=a?nCl+^p6Ys{3}Yc=B^BlyBlG3G zZ&~zgs9G2E{$*H(NCa!FzI=|>>MBYo-?~Yb7E8`LdBElgloOzLpcu1OoSHTqm(LppaR*Bs_QDt?dBDjsO$iDGFjfMYRfSh6IM9& zTQAdp^&<4UxF{lM^g^v)NOTsl@V6yB3wq%NL7X=rjPnU-VaggW0)xy8)b1gBpWS5d ztJ_=zR_P+h%UX@|A7e+qDr>iVmI9-A22?TmxD0iJ0|iaBDFnj)(Rkvy3T2mtpXCab zceQVhYeyNMXWqD*V>t=M07wPzRM#=1*p~2y`$2{tp++K2U5~CR3q{AQjjgitL@9u5 zr78P!OFyo8Vx_X?V#}^7A5kYK1j2mB`S$hJpy1+uAhs3*4HC?GvtD;%`J48!C;7LG8AqGHW$SFW) zwDJtScfjtacTr@t5KK8hae$)ZD@U4N(KYHHPbJESNHD*R;$jN0fWcB=y(JVo5*<8N zRnL!o1-6V_c^SIjkN?+O`MYop(B z@L}0tbQ~oFM7WE=va0?Hj<4ag_^`o7!`n4rDZGUvy;3DHFL!C5-z42S1+5ksiAY?y znz24#AU1=daWCol(+e&Lyk=$rE5W!p#so#GO!nIOn07y&s2 z9j2s6vVeLOOZGSB!O%@#=W z`k|!^M=gN`Q9Ij-s1hl4m}H(N8%iNOUy9n8MwU=H8WD{&p6>wX7z^5Xog>WTydD9< z=dH`=Ryl58S)rn_25c#*Lf5oPHo0?!J6VaJd19d1%{VxgZF$|*JPASqm<+HLTj$FvJ?!@g?T1HAi+)c~Ps4ba@69lfOfZHxqSbFm3xvI)D5Atb#_w4zGyS^ao zYLJ+y*ksA3YjW1lyRj}(4MAu>Qwi>{DitBJNBa2;A*&%NFv?!Q`q9)bJ_rIE0Gq0U zYZXm4ZzTnPPt}sN`abTv5m#EWguDb({+2S~kO4hfue?F;rPrZ<7Mr9@QsEIH<8w+X zD^)}h1eO4=@t9WZk{*X%a6u4}lx{YJ#cGX_hQwGHWejdTVE5x&zS3y*n1Dr6kd&HG z-17c-iZ$J*q3W@u#!%w3)XN#vM<{BqDi$Sl;;JkHLktTAW)v7oybc#24A<_j)OCsYi9nPtFAUdqrH-lc(nmi*6Cl^B3WI5b{m`c&}@NE z77M)+f^O0n(USi^cUlU9K#2gUz-o-qm^`B`EQ}`X-@MDCk8W`RK|($GLSbn8_W>b) zy0rZ#p(ZFa=eGzB5?-r}RrA2_u|#FK&+@aKeBcv_<&X%(GselsmQ>PpL_lzt52f<>we4sIqtM36r3jLh9L3EQso}Eo>=e7w=o^WK)yR(O>Pa zGjw{3ETAwgcAg-KjuP%GRl~_@?9Y~h=qoT}6cnTsBups~oM!7gS2_Rs%dqZReCAk; z@UCR*vnZBUz%P}Sg1}Q)%-TPCsR4#l2G{Q}{PaF48C_f%g_yB8K~N#ri|8o!K;yTQ zGOp(F>g5I+=RE45k&nQkKt8(~;R9Y4CzCA_0x?8UB0Qxu2pa}`rG6k0{U2Ke6?AoS zVQ>_r@nqzMq?Hk~jSVNC?Ym%R;wap@-ftsB-r`oGE>~5YvyB%5_1ueKqQrv69la6+ zlB)IOv!#%HKSg8clA#!qv%zW3zIB!K{Aox86Qju!oFK4`>srz;jb8W?6M-ylAr}&i z^sPdMV;)?;&FI5jR!B*iov`p|HDfG(b;z?AEj@#pg)hpg#?fB2CN^aR%B(dID0mjC zYNwV^#==!ri2Mh~NW~JxmZ8dK2&D|zxvCvt)(U#QWN>nz*@`pZ-m)y(S8ZRt{Je-} zR`E$mIR8RvX*C@JjrSr5;gATHl%g>n*HQ3L5GTP!9>^V|N=0*-Hwr^S1`L^^Hdg3e zJOlkU#uiM93}b`B;xpD8uV2B3=-+u-3Ib0~2ZO?6)4~|^V8r%UH<_?a4?#jPGhso` znr$&M{~XU6n>~)oP|l*N_u~qs`Ij%6hNbigSK|^0HT_k>&$D zk0~|r{N`^*eSXYKX}tU+tmB8P0>VH#Ur2mr;h36ZHl;`?k6(`{59bMDRC#t$>6vAu zN)!L5e(dtA07r#h2<1O#e~D5*JsDX-EL3%VTl@X-s0_R`7GWwa+4zG=4YAX%^wCT+T|Yahnnr3flAI=10`6w@TnBoFFK?XRDsb=15294esh-&)D*MOt~6t z3Gwt0>;aF^2;Qah&;Jl!e;v{kg`th&@b3yj3AKilPqwOiW){IL_2!OG zM;7BvNaJyCuNjvML?B`Y6*03~X>Ji$?Q~rnVi6g4F&5a02l|l9g$-N6xo1qJN)Uw5 zQ4?9Y^_dHzE0iBMR9z7w#vG{|E}~ANXrnkq_jlh$e)oH@bso|Lg{9*gCsbhHVrwyk z*TxZA^F|tfTvjK2GFl1(Pi`$0^o*pH74UG6@q;~bMo1tfA#RTH>q9=|Q<$1XP?o?6 zkac*pMI`WqX9q&M8~_4i(6thC6*M+qZw>4>4iP-|c`XlISp#uQy@;Hki)-aPKazm( z<;!LC9dJaKwJ_(Fs_R?$^)ZB$pFq)=$Z+0@8Ai~M2=WrKGO$# z6b#*ObkGdS?T>gd zW+zaT$B^~Bk7%pN0Eoz)A@PF1ZFhVL?KpF~xlNp7EeJlggdD}nrL$ao`xRQ7Ys7&G zqBJ|_KtglJy`*n~mV&^O1X{VJuN7<$*!lP?4!?Xz!GRBp8iqc)JySjTYq6ZjYcY)m#fZ zS65|b#VL!5$cQWbx*vW1Cj?Pit!~;gBU}Qb1Of%h_8yZM!UOu@L7EUu=AhfkZX=u; zJITqK$fb3W7S!c>Pv#T{V*t;FyLe4lm8Oca2HMI=WdI>4WF5%HxbLy!p!LyMwbM-5 zxH3TGKw~^G|z^S@l@FZd2x2wTEbiG9- z2<+c=){dzy6F4B;tstmD0-Lo9{+x#igL*709MsAh@cS#EASA=OhuYUKpw^e5mssT6 z>KHR2a2jbQ2pmZRD%YSTZ13~%vu}9BS6m|^CPb4z(H(SJEQJa8j_;usl$C03+Z);? ztPzx0Od#po9!lvP8@1tYgl9;W9XB)iJ@{E2_&gjod3u06<7#rBX2U8W>iXJA$CS4W zp8tqj&gZp)001BWNklyj;c#-lyyiRB8J#!RfVNBIti$ zo|+oe+UF-VVdWktnwm+ZwZdVy_R2>DK*!C2Hm&s~SX+c7B-bV3lkTfN9nApojGhm@ z^n$==aR_Y)%G?Tq-F-%1+(WTXiw+7o(V%{k(N4ZUhbumC#)7&swYj+cuLj`&-|fq2 zjV?9HD5u^^j1iu%IY#2|(xnH=x{^R)7k)-<5txd(*cSGUay5U}hBohY%*Loq_>$Za zjn{5H*Ok=xnUiDiyr{B-WaArc@BtQIJm<5m{3TX*ZZs}&heB#sKMaNdL0ZLMy!)np z7B%9igj&cpJ!`%j+L7e7dOJ&M6kTiaPReQ@i1&BdM3=1j5P1Id(iW6e&BQAziA)N&KId2e=KtdBkAB7i1s9knB${YYKkbG& zu6yL!QC%CH0ecvBfr0zG1;qHYMvW#RFAeE9#TFxJN>e}#G6IyO2nA_iE33gnXGrw8 zgzW*L2iTg zpRV?K{!}6WsX$i0NKphUU6^av8mxiRrOQfJiET74Q>wO*j5H7lXa%G(JYv8IjV7+f zr3Wk^Nbt|m5HnBG?$~kEweRPE7p?N;JFCwZXDLO8dHM_x1oT*8o>e01=jh2-a9iun z!m9Y3Bb!7jTss@@l2Wv}%BS3756$cU-mkOv;}4*-!2U>qNUH0arsD_!Z#*6Ab@E3# zBVKxozzPEcNL|8UA2v4;TYD^_SfT|CUB`i=_0PjBh3}dK{^1ECLKM(L)5Z`|G6eR3 z`=s1vpP@92OKqF)fOStaqRSd6MI*-Gf{UH@9P|rPZP=tw3Uq+Wjcak8ogF~6EsfH1 z0fY*aw5}mH*1m_Y@HgSzLk9-eG(P##CsAq8FUk?85oAcl5p@D4tO| zZCYS6u$+6S3?&dTrX&Lrpbfw>lAMwOeKN-GnRb<7Aqbo$P{UGtF)C>MF--#c2q1J% z$s!pE0%BKZTwk%6V%v3>Xx}N?W^UQSH;rJI0T7a~X4_F-SVl)NLM6zmJKoXH%nN}h zxnF0D{=1=B%OxXQ5WJx0SVpW0rypc!VKikumEeZ>@7>eTC7h%k-{XMYKBz86cGl z7P~-aQ<3xwQ5eCnKcb@rEX))A@G8+a5&5k#kNAQRO%Gd5Y7pdT3Irt~a;XJj;I6R| z#hfso9y59ZN+~dbq;$v5C@EaLCg8im(R@6W2ufnow!}oDY5^*Zec52Kbpr|Kz5&n7 z1VV-sMhY&-KV&CmCi7{lqvFGx@B;E=vkmSHVBcMzC`YK{|!R{di zLPA++YZ*>a9osm;19+TiKBK3mmsSw?xSH`Lwpx>ZeUJUm@1g0FU>t_xgIm`3<`~WRW#e+U$_A1uXCbF8t>|K)tp`vDRVnotp@$K)7-~ z7daNVtggcX#m0bAu-N7G|M;f_|KNSNy@~kb9@~HXA!8o0$B-5w76hVn*WGj#**gd6 zVr4#XZEMVJy?sg2Sz-BmufdJWFyAHBCF9bNNM*B{o?uSTH2oJxFO?uLrs2!=Y_9vNdc_fwPL+8Y{Us+Nn*i!xdurGi!qxv|dm-}(t;1Ni7OHox9t@Td2ItqF?&0$La6 zSiZUeot8b0&&={#@?*!sg^qC!!0f1vST;MOgqoFFiylq|ioOzX-j%(Xy5UT6>!spO&E*k*Kk=v)f|Go>Nl^p zFX4l)RH+u;^fwsWd!Xr<4U}c-(HbNYL_qxZhxGsS?|`i;z*wMBD7se{S$X9O^g316 ziZ~J>Z7QqCy$dOb+X*bpvqy)ai+@;6^u>7xN(2j6*J!OSVOlYnfxvUQLkb8?^`Ct! z0Vx#39avpwOyP65V28D7pBE|)r12l?)f*eD#HcxP20lG1T z(Byy+Ga+ym=v)f|z{l?Zn-ya#Kb-*3nn-JcQ3jiRvY-8&g`ICOY>^NnNUOv6K-n}| zYxi)+<>Q!@IPm$Ttj*`p)M=z`lxv@pBecdufGQcOl3dz7q_A)jTGl$Q(lmO<`lctA zaqGYZm6H%-QiM{_>YzJwj7Zo5Vsc_aGN&)9r50=~KrbW@4Ypjtprs*q=PWVgE&z$L z*I@ASdV@!m09_biE@L19OY)Rm2C^RJzvbTncmJoSBR1^d^7x|d5a=bui(Pz5tU@wm7fp<&#FUakh6E-eMIj1FY|-wj^CsV!gooLv731S3i$5f-_0A4Nezmz;n-V8|E4K&Q4^xlK5HKcj zy7lv_->7;zU?_Sq6 z+Kd20MvH{7w*;nz%mM_V#jB9uaU_JcsGiNVE3X_>Bw?f!+p5K2)aUIeM9y^}6bNzz zd+upa?OPxyTm?aiV3(4ZkTxL(h%mGf?4j7kuzg{J_`RE`*RNwbwt0Q51ENg0?61Se zw#syBES_0C!1JebD+mB4*P)qcnJ6&CNCIuh_Xgx&d_zPwQ3^jD7dWW!9c2rgjK1wL zdW7`JfsouS=`_^j%AkeyuKR11MxcyvMNhT4WZf;&S_DqBGzdfxlL4j#f{45! z>rUu0@AA-<1Lre{Kr>RBL`akgkflUQ+31I0SGsb7Kxu<0?S&{;dO%S^CeS*7BtZ_k z2to=pV@8BLPi9e{sl<>6P$)wn4am|04@p~XTq;4Se9n!j?-y&0W~QJtnoMW3$`nM2 zpg@umpy|>f2T~G}Rs!CNmBKM+TuP>g?P&CuhB}qhG?O8qkcI?Fh(YE#WI1glEhKF$ zBWS=UO(-YeA*V}9>KcO#!w4uySpV_sB&$mx1Zkcl!o+5#IRRi>C``cxii00#3jd3t zb1eua0*Y=TVzBNJ3@sE0gWm0P@6)dk2d4k==tXNUikOm|A^Yqj7|~`FT4t<-4e42n zK%lgAg1`y|3oBtvIS`ZiwWdI^89fn1Kj1vO7NJ3I_a&m+Vtv(NGdfqjf zJS@pVO%NDzVF<#KQbHjtNMGqQrQH8MNx=xn%9K)T@-n4Wj=?3w3WnHHwF2Q_{ftwM zOKDwZLO_N%>S9JplxXhzPQ&nPJg2}lU&l>tISrZWUq0EAi)Xj$R-c;Bp%uA!p? z9Fj&Lq_WvMd5$PD0;6r(sVIr1A=H}GcBVxDB`E=lu#mJ;5Ltl;(+O+9U$c?e=U|*d zt!u40&>&R_EdjAXL?t05EsU#Q9-y8hI*MqVOJImX+f9sogw_Sd!AaoYN0op{9B4bx z#z0b(1X9qI5_CZjTH%rceNqPG^br&!6rn}<52c|iZ4a}+cqFIE$da#a_6%K4hydfP z4T+Kl$}x=kWF})o&aT^M5@y0CiPmYlHZYu%BAjDrh+rQGHkP>f-s^B-!2%Wv3r|t` zrv#NyYtjKhVV)3XnbCJf=T;CLM$&+C@CMQt=%=tXAicZE?nn1ndx8#gaDtjczZFyO z$DzX(fQR>b^1h{8urUEK5dlGF5PKO6Qd+GTj3F?#3b}bwpMYYSm$ANFhn0iC6%CaL zQxNC^`Z@IXklC1^EF9CZ%p#@(M5xptx5toWgzds|{i)QEz*LuD&+VGu#J2STL0}-% z$jA`1E%S@Xk)(K>e*FSih5+U|2}x)(wv^CFVH!1FF3w{bJsyO6mJZo963Uigl{r#s zL=0sLLsA4iKogQ+A_FFaBBxab*5>YU8qaQLLXH3le`=-U zqnASvQ~^Af5o0ug(J&st-X7zJ+w9$b$ogXhK|{Zr`hL=#^dhLfu7ZF9$%yikP4d6_ z0x`%5FRcL%fV31;Sjo+tO&5R^6(_N-At?k=o>_N7mcia0JRC9iH($}=5iu4Yr$vV` z9b5hT(GL0--#}{_mL9ok*eOvbZA!fGDJllbBy7DEdN8DTw2l1L=R_Yqz_5)Vqhz-# zD0>l%Bc?!pd>65D2U@X3tW9E&5LCD5eM6>xxoJXf=7LVBu2*0oA2ugTxJlW?&qv3W;;DP8O6?h#LgBfFX4-Oh_AP410CiG zfiVX=KKPfSz}9$s7gFdFT{~>td=*xDpaf}NQXmKeH_ekL3*#E05@Y>%o`qfwLC)VjD zj3LwzXhBdKl-5Y&@?nH~d8TCmA<K)_7nPF->2Vpf#Rv$cP$|gTtlGTz6&A914sjb9FGb5W1^qmW{+Rcb8rYHxUl$( zsVt7j`1ybJe{lKp`vk3sw9Fw?XldJe3a4}kV=$#bYmG7XT>v6CFwSYCna^@||JPrT z^BD`kJc1qWpeYzJ#{6-_{KXZi8D@f%F??O-OEiEMTX9{ zDYhT69=8Y|^x6986NY?4LIAYznNjsoCtuQmFWPugz#ch4yTj_6H=vzR3PF||N@<7y zBSeKjJF6u)-E^)6fe^wWUQ%)K0*x%4Q(=_x;Aelq-T(V9*q|hI+0);aJac@Ua5(O| zc#_58&*QOoLiU*hGCt(azuabjF<>tekb&HXpfChjSAm5LtNZ2>JLXatgsvHkaV$8b zB%vgw1n#kcwFW{!(?`w(@SGal(s;zwVAV)AJBOswb zaL<8;7Z^qr!=DH`;~~jyl%NNstZ;{vF)Lj9>HEZQ-h^NgsTD~mD3z>i3oR{*?Gawo z8J!Y3*Mb1*pfN6gTMI*gv1n^Mdu)CD8R@USWDVmQ?L8ZM7DC{#{icsQEeOCOgARFw z<`JFkfQXL6CY3Ja=Mm*g=kK+Qq)a{1zrpvj5(M~QX+%NHn1rnAchI~ae+_LmDcM3Z zM?hR}VKkz!I%nv7{vO??K$EAMB9&+&LIPp}LJT1xF)@aW(D|)Xct&FTapc)(QQc>Y z5gTNbJaXGeOm*iTuitL%@jE^2v)83@X#R?lHe(WZq#Vz1t$-afV8GXeXcVclggiLP zBs!>P+Y*JU(jF{kYkg)LAqp@kN?POuD3rU#>7zAY0XP^6o9H?QA~xu~`zEYzfb1fW zgvuZlXd^JvhKDSi*YmZTXLK?;_kzHa0kkQRQV=NxSxWJ6li|ZHU_|V6nA1*8@9*?y zPXQL&-uj)boe&TbI#eJJ5oajL$;n759E7w#S}h1{K8e5(kQ0$Ri{j`E53Bq|ER(Q6 zTz%)06MeF(tvt|ZmzY>kP`aAo+Bc2sc~&yrZ2h~Xrh}zsk&=>tIyKT-Qzf<>EJiq? zARG)JG}Ab>`{c*DwrgtlOZ`hkhH{#G;J^szH^;!R= z6B|wOYXM2Lb6{D>xTaMn@V%x@W2~F+xGNtpM$=~QjSUvAtV25jWn4VKU6n^hZDx(m zTtlapULHXJP-=uW=y6K=^BG*nV0oZ!&ZL zn`I4k8CIQPZW(at?0{*XyWL+?cM0~WlQoNjX<;A`bP$Btsij>90ndsiHC64>g=!7i zPsoxS-^T;|Gmk2F*>L|;v7$xPj|9Sz~G%03T2^3ALCqipZWJ9j+Z6YiHW zwP3Jzg>=uNtVYU;6^{eznYMNUK;!!gTgG0W!ACNhz>8&NhxmIuh^l3+342YpCF+>+ za);%c*9bROK-r?V(q#z?Y<8a1R99%4(m>SrJfqV==S~nzXsQj^X}b`dw>vB7Kip<; ze+xxQ7fD1h(V+fC(BmzFN&l^9RJyUK>+264d#ag@cH7o}+br=WD>@uE%{RdDwt+;V zCnk>9Osx<|@cnLm8y7?EG51Hb%tcmC6X_fiwUF^bLsSgQ;@{Wo^Sc+@2lMmm;H)QK zMNyAJ9jcw&XOg@=?gfJn=Tfn9d%ty)c`rh>`*r#`d0|isqUM}26IWjM8GhR>-61}#_+-sL$We!70p$w&P{iuy`1&an@B*JA>O^(})P7|FgLEssl?-m7i zT5DlnLJ1-%5rt-N^AY>^w}6ZmAQ>Ui&k@t|+fdEsJ%pRpm;;P!Tp0q9)Ayj(yDe)L z&U&!fD@ZGq8zTU4=(XVB z%$ox_qf<-gN)Y%usmk(nplVuKbj#9cqzoWS5%)I{UvJW8L<Nbcm*fAWVg&1GQ&1*9FBm$9(b$n}75tj6V4@uApf#N5%*^It%8o&(riY=r>0# zRWtRgnfQHDU`eM7hAsf?JF689_G16Hy1HfAmT`FYJuR5(z_HB{@=R*lv0$kLM%du5 z%{FRc0oB>`HKdtOw)E$%wO!B9YW8D~;Y-PyZlS~3hkr@6fW^Y247V@p~O_n4U{b=bWo3nK-U}_LupR zTl^AO{oqY*{@(AvYnL%HBAAmFND$Y6NE%yE=SB9^!k7*b%!I*-=d!H1Feysid zG)MR7F*=MMr>0ET9Zc?Hl_h!T8UV=1NP&I%Tg++j zAbd~LZ(~N^J)H|d05%3tDasxbf*9g@pyu(|?aj4m`n1cWN2YCK%s=J|ED zo=19qZJwS339JjqhZucfzZ9zJ!X?&dj5z8kLGHVMGJD*04SzFF=meWAKjpH{VKFt04ODpVF5BjliREzFHWjf zoijQMbWQ|;%T;sl5)h>ZU7(@>SsK{fW$VxXmch?HVV(i=)Od{cQ*mH09%sydn-Hi! z#(AJ`Q`Yq|rV@VJGPrL$X3bgz4{{-Z3||EzQACWKAZQ`UfPFf=`fE3t|Ni%2aUQai zpcRrN2F(y(bZFxR24TEiK64SB1v*!*p-L7Au(=Md(RY6U_cj^d+eEWZKtaL2gEpHS zgrjmlW;CPoLXTt1{}mn~$P^&YcTbXMq`SOhg` zU$!U=O4{mG_WN+}0mC~FDcC2*5W0pbUSILT;H-uuXEdXeQd8lnt35nONrij;s(SCLNlfjMdv~g*gnKH&P$1=Py(fe4HED6NvZUj zQ2+oS07*naR6o1L`0pR07(1A)*!nf8``;ed5_p`jc+&pIT_7i0NHg<$Mo&jgMJNK- z6+)m$z&VHnJubcfCJV1zwHAR28Hs{Y8k7-8VY^58tf80D<#*QUTnGXWcBQcvfkoVb zK!Pj^*x6(7%Wo)lHVMgyP#A1ytfnBa?LwY#D(b~^7#ziMLS#=`CP$U|A9X#mu-uHk z4H@d5YAIlAl$EO?fHo^!y7?wtxr7NrhDFJ^6`+BZww=h3g`Kme^3BfZ4AHrY5ddgo zD9{cC-(d0_w)W_MdY6&`F_Oe#=3B&fsx~UK={uvBiOj^y*4sox_$r*jA#^RV2A9_` zorGNYc10F`TL>#cd=~~`JJYyAve}9{D|9|A0#D9Asom6;7)UdUtvv>R_5c`=pa_u| zGPeyA*%QY+%_*d(wFyq=-OT8_qsgSvngq7YS@-uG9b+h!a%<}RC2HJlJ= zG`(zA6VFW5GePG<5D4KiqK(B#iAzH)pf$`H-FnFGho7@=?hr8|L{N|d8NLJTNpu&# ze5Pv-PLd}vz0H&UKI%H^cfYhx&eZF+GtZxzEh=YpJo+|c=8?D#nW>2OUH}^RyEZVmPm=lM5J}PGMZO${J^GbD>+cLHsinBVh)H=C~_wRyk11rlmLNt0%bHLK;~0mgLV7d@Y}Hw?_$<-6 z$QlwtAf-e}iPRtp4S5Oow%Pr~myFW~C`N>2gyaMm*WS@KZ>*bnH;(y)b56$urnf&% z_2$#iQTRt5JLX`Y+9w}xwrpQ+^0UTbZW;FJlcSh>Z;gc;7h!b{!pE=*U(#R232NxN z8J!I}Hx>cl{5uAz4Wv2Te?<1F6#@~OkP=mOW>qc?4rd=U+r1zEbWM6*7QwToW~^aG zF9%I4J+&Z!3`0PV<<~CJx^w|rK|^IcEZkhH5_OzlMrVo6mmt7b83V&1+h5;h|1a*K zNJ&rxjh?Lal_1FUn7-MgEi||O>Sz4w-~C$x9x_j#ggpcjO@u9g@nK?X71$i=$%N3uTqehf zBRoyu`LO+uI`{N-d9z+l{eBpornUKPnyt@j5ROmveDVJd&f^wqMD(~%O2h^mzyFVD z|Kta-vP=<50;ClL!Z-+tLmZ>tTBu?M(^oK~(@N(`5J(A1l(4@?x%mive}{xIZ5I+Q zDbcj4pmWg}iitEjPfuc|Cy56<4IQ@B4m*Eyn`WE&qv$XuFk7bQpQei+>-IEuH5AxN z;t>go3&dA1QqK1(CbUZ%oz1-SLg&gN2#i1!V7B|@cee>h=@4McV?4TOZ6Nz(w^~1{#ldmH zUf*UJJgZp5)9iQpb+uU@)3c_7_O3_SvQEVe!9JQXG2+!_Sm>gShSIp=G1G_vJSH$~ z4s!|3p5tk!b0rAKOUj)-+1)Mrzr2TLmxPjlx&?Ys`Ibur+8Mpg1iG4D)b#x>81+eN zBwj3e;DYJOjv0Mhs$D{=@dBGP5u-_nH`eI9dkrpKKz4gbsSrlg%D?dQ=ctMnf3hio zFtZ*`HJvX(U?9vi+1`lYQAR>ROpbE>SDS*Mi8?B&tBjw=bp1BLGb9X%TvP51kzb_5EEXV*3-Ef zmYLSsL>7kF-nRz=04j(L?~L)z0~+7gzYDZW-t?a4$@~)^e#f;h5d?Vat2}Ux&1;uV zOZ9Nbu{&o8D@0dpHF2RJC;H71KQM{X)$#!f6WD&f`Sm}xO0zz4BJ5>MY=A!G6IGO+CRF$@_VmRtj{x2fD{DU?d2K2@w078q|b!FnWj@G2u@0qfo3Tdv{1au&RPKl#@)l|i+P-0np6}7k^n^;O~4p~q{rfGmr%V9 zV+`6DlxU9e<3@V+6wW%GGeICxK|VwMup&484Qr1}*?*QdC4XVmVY7fmNwPEV@a z^p=9v{4xLnViFV)8qJsj=o2!e>md28wk=7B=#tQ4>7Tz%zL22iJIIADi&rl~Hzw${ zQQa1%9n#GTOqMg=ACmS5=qyK$GlCEAp+0)R@Y7B5Pq)d~WW<1!J?04MB2lE27)oLU zAtfalV<(8}Z1)g|sV$idoLHODlW00Q!F3cMAR;GVL`uv&Yp-8}`8i||QX!Ix8Jm9@ z=*$a(CK(|hjEUwK5Nitzf7xM3Nl02{2_;Uy^pMTR$#g#Kx0A#KzAKtG(5H*dyf$4@ zfYSgAT4(}t3WkiiOU{560ShE75r2P~xmVX&xpA5J;tHKNuc3P_WVa2i7Q}>pmzAd~XZxY{T7$jPGx;`SI86eRiLRfAKXt?6b!s+KkBAqohxl z6(j*6l9Ca5!|3KqfOf zYjmyzfkt3tK(w$xw6;P)j{%w%5Rp1TpeGpRbinl_^rDwSGY$Z?kN;qBP*S*-Jbf}s z44Rmfgp`Pw4HkcLjozD=nSb{>-PbM=zj6tdd+1(^JP9#jKrTcTwABr^ps;{sK??!{ z${0co^j6?{2BVZ@d!GyU9?`n@koe9$f-fJi`QQ8rIomE!`G^sHS_mRaG@8;q70IM4 zY$gOwPED)8BaCVkDH6dPbFXh8HdY`BDN2nbKuTHfB%On3mOXSX>3j(SZO{gSxp~^_ z%LFWtGG;`-%3Nt?(oAQ|JP!tQ)G|8h@;b^I@SpvCNgx6=At{O-ASI&B8WAhJ`UmgQ z`N3-}UAux>o`>}%SnR@l7h=Vr6_7PDfs6)eKp8uEEc}2#2+Jgl5LVC#LlDI%rJ)DR zEf8P2Ogv6mE(*B&02ULY!)YxaS#bdNS35 zz$fNfY@r-U#5@a^FTwgMgfY4VDJ>+RkuDA_W2Y!XpHY1F1>s+P$nAgiGs-{rf%{DaHq*=BkH?C#fo)q3<=sXzIvy!-R+hn_ zNP!eZN6gb+zX0<+Pyra*x=A1~0<mj-Z8Hyzz$k?<22qv>Es;Tjyz&a+!U|U|U1jmwHTvKCIotp9 zU$VRZ=LGaw2D(_QU<9PtET@PK1bayO)l*a)zcu2^*tfyghFD9=P@veQAfwCWe{qxG z-PfVhf*>T40a6AwiYA52GGc}jagOQK2?F3m+_Pz}N-04pNCMRLHM;M=%EN#6_hesg zkr5M8fXk^bDaonb4PXP3O)dL0Yd}ryKgOE;87aB51B@#k8)5~4;T7h-f0Oy&`9AYM z`2p&!x5yUe(2FhdB&v=!iC1q3sloXIV+gcC8s|o{{-D}*KwXH2u`VS1c0LDPd;MNf zMlDzh5%XQTD+{!jdl+F5|8>cT4;k=)2x}SGR8vVG!GIxiV8Dk1YbO~8!{-^J=^3xAk z{NQ`=>NRNfp!CeUW{cjPG+B#4r4jm+!jcAm=U_dN@CR$6L^PxF60{N&QX-;&WMv-t z{u`_d!{W7d9{iij-1(!Q(Pxtm`z)|Pmlkcd8MAW;BR@`6!OO<}Yx0jm5n^bOVlcGm zyt6|4!V2_ym>@(;i4YQmZ$Mw$%gq)9r&~>$(MjoC2?EQN6evNsxJY>8IvYQ_$*rH? z;r2&gk&+`_Re?dH$R}zFj?>uXG?KxPVwrQmGC4iA8PF%7&2_H-rytP!@!Pb2{4Tt9 z4Ho90l!PXr3@4|6G>sxs_np960RqeLwfh~cFqq~G(=ZiBU3Dp;P=XSHXh+1CRzc?^ zD|4)_%+pUK_y705WrwdQ2uO%%(W1rRkcs=GBwusLrxq3^lepL5CKKf|QK>1m~Mx0*668JP*>q9=k3a9Fh~UPs9!(W@U}V|L8YZ`~80m zm)GFZ8njwuMTv<62tp(RRUXb7C%$zKVk2uM-|_={*9U4D1QbC-u+#^#&q}y7RAcKu zQX?P;h%R4(r6rQN1unH)WJ^81`0os3zM`Pd1w!VI9E~Qk7Y#P1t1CV0Lf|mB){{hF zQz#ceilHDNVxE;ZuOe=2z+A#OvQJV~`_zuor)l0aqq9TjMi5vMXbpunl%}NBiivMr z;lfYer`+u`{Fi^jh(0Mk9$*L<5@KToK41O#XRE2nWFF^(eq8+FX_%BRuF8Qi1v?Db zqs{W0@3QvKe$2*i{|G*~2}uM(kV`=k1Q;dJXkslLp3Gy&Spiy@3V*D+-Ue3v#CSOO zQQ2@G`7Mmct1%#TT_KPOfes|97ZP2I=|mwL81_daHvgBD2YgJ2fOQmO=h}N5c@cG3 zitSmXqtb<^|2{fJIdpL`LrF;c<`Rq7FTz3#q-KnAFf4vvO1A%~`9B zCLL!)vBJ#FBd%Q}4nxAvzajnnA?d9c#XgZkvyl`;JS3gMzi-A5?5l=go<{9#3X*0) zSBP!!ts44%)|)S*VS}uod&_*Rh*X*eK|?};UE6tfBB*>;#-r!G8LSUGf=dW7!_DZ zvoeVhD~rruyTbDOHyCg9xV1gx!Pby{a&jaAizpIC3@ETEi=lh(Vj-0kEhG^%81UG= zyh&XP0gLCNtDu|BbeD~EO#dpddGdz3`WX&FK!?y+IVCI|>rK!C7u zBLsn;wWeu-9pT)Ze(;A*gc+SyIzJWx;5m^JU^HY3N+HPu$q>Whm36`}V!a)sRu{Ygydf{$JE(=H{Sbs}sS*Hq`WBdDdJG=emc)?u-{K@WY*bXHj=Oju7zU*LZ>x+Zz-yF?&Ep!u+eNs7p(bD9TVF zr6QzmETlMizOy%SGdeDvFF_!%rL6*x1}Oxx*?FMdf-CFHw-e^qE^z6}B{tvwgnK{# znBkw@Vz+qc0Gh`Hq$q|+`hcblEMhPYzS*amHr+K|d-pjjO$UW#OOuSgshvvaI%@uA z1%ZWzHV8tB#<>(3f)R?Z*=2$6bM2q}koE7q2XkGt2BRfP1{l*U2|8FDWDp2B%`(LV zCpcUW6jkI{9Q^S}GR6vQp=4#vP3IqJDM*DNmy$d%v=d(UlKYuf?&c8 z;Te~KW&m`kOS($H(i{YmRt9ja+Gg|5zhPWHqGXGh9x){a zIRV<$WDXG+5^DF31@NjHt*A;=7$-2qgfQ@o-hWn&V;$@c=9pt+A0MMgQS7tGh2Oft z#!r6D)(OndW28Z;07+n(>trtI5ucC#+EiL*=nrR_?UIoR`_a+HxkugMF?b5oq zLf($hfuK~i(gTGxy|U-CcA7CkkUe<*+H>Q2Omp_8IU^vbnWM_ z4Q&p%eY_y>pRGPtuQ$)=3+FhIN36sQMYwi__=DGx@4W^WFMv{ZaXW8s<(!8KlHJms?R?;#uZt2c2C^xF8R+-0VKqW-`RAy?2APw{9S=tdRu~ zrJ{;mVVd&YD-exBvKgIAIv0Xqf`n(n!tpuBM2&YjNOA#M297~Xgn^(CaCsdTmRY!d zjfK4d7yscM#+#e$-M!E7v#)vd|NK3BclR;0iRjX2mzW_j=>&^xx{MBXL#tseno!Zr z?R{|AHkC01uIFz6Ra^oWqcBKb`Q0C|{KL1c5C|2Sl;lE!unk81_DwQEaJ&e{#9qhT z?szn9q%;i-X)L;_$FV4tErbd>5%INkR&U1L@kb*!e^Yc_l@1)V=Z0N{MyLexbu5`l+QGp?}8!cp4{ zlOPbZGf5J5U?l_r^1WAR5BCW6cbLC(pZ0IO#rW1;_P@N%_@i44Ke|i7Llh5LKr;sf z_^h1CM(t2DcfYope|teNy{$(8jj+IL9v)6$TaODAum0qRgzvuvtBVYENuexOOdwQ4 zOP@|OGzTq$=cw^jS2Gp)^wvTE2qTcfRF+nuAPo#*Ct~HD*BHKak=#(;g-RXIeE zyF)h1%_h@=Uly8t+J-*+be3Lc{?Zz9uFF7qkQG_Qu`1^8=SQX8%*=uFOlMvY92U&- z^ElA9v66Q|c#YE_FhU}PEu`{Jgw*69HGwDzlz>1%JV!X+Cc1Ee*7fVKxl4NIK6{^i z&G3smY=3^6;ivc5{nc0GJVKCT=sVvvv}w^IAw*IzCS#w%1$`v}0Wkti$!H?iC4wp_ z-Nfv%x6q?cMxQ0r+Eo@l_#x)fWe5{=nxeEqNa=*&6n{TzJsn2jt|1AhHwkg8Zt1SNdk9qXhoAf{Xm=Iexo4DR)))w&H=#JNt z^t6)a#myK|Q&IPBM+-Bekch?{vo-V?9UdZi<6t#Kr(G2`6&Q?hh4vyu#BJ#IkQY{o zuV3fF4}JuDV=mu+#OVGen}77L;jcd?zdd08(Iz9dkqp_Tpp9UG78*nDkX~B!2v9_> zmHY^skpz>%V7hrui9)c*E&~eIx%3A=f!AL{w_6xvh@&<}yG4Cc%#4bhzTSwyAIbvE z0WtG<)_DRe6-)glh8y z)ArC=BOB!q6x2xvlkaw$2!$XbrSED1I%ouA+U(O}{rw-pEAN2nB6WaNw#AQy6{pJJ z!vF2RI%*Eg=v>g*76gx7*|p9juu5db9HtUqswr;j?3ijtA<(V>N-M7$DfD(mEM2n-ps5GD*=Kaz$V!w)#_HSYH2LZ zC_)n+g~J}tg#GSU|8ssGhhH3iupb<@rI44F+G0;HWH)yLAc(C{RhfD3JluP8DHI4& zO|ns7ejV8aGOKb!=K0QkW`6c#h?FNb=V&iF#1u|13;1XVGlGFs(>ypdCc zZ6vfi5f}|NA-{MHqplGTGCJFPtbBW))~!3VesiDUok#5cuP=zd9%8Z)DJj%xx4~%U1%qW)o5FN4s5v`NxQ2nQ1t*$gK5XvB7*S@hw*M`c+E#j5;$=nx43u0 z8jFb6#YADWkXlgU`sF+oA&rE&q(`UIp|iM1=jvrv4~MXGK=$j~3?FRpiNC1l_BKIRAg`5^SZFWdGT<#%PE)ZS0=!l?p*<&~26uz%Q zJ-$;V1?z8mNE!;cKjPO5ej43ehu)P7%>I0pz5jZPlwnbFcT7ZsqYug1Ku9=n;#*K8 zXj&Lg{)RT^fAtPrU4oeyTCt0bS-~2?sJb3mJ%12P0n_82BHMn--sTpEcklD$=2t{_x6og2 zqW-vrWrIU@(DaEgMBd+RfG)EvUA@kgOV<$-AyR3h;TKz2IyRgl)Aniy@53Y?|9(2e zDn(~$p6=>7cIhB!vZ4oRc`vGjpw56bP+nIVD6U>x$dyVtAS0v2ITl(Mn0@~`+478g zc1=zk6_Ej;v?iKN(Kip%O$2|2m0x}EPjm|vUvfoh+NuIS7gpu3&J7jk&Q}!dibA|F0iTyooFzWH{-=_zExFD|k zAI4*mQtM?@o~T7=x7wuH5IQZ+U%kwe)egH4cUWbPoc$xS8v-;(FLPStT83gkM2nor z*8^9tCqk2x`y$0IJH$Lfap}s(T>p1}2fZ2RXiNk#f(heC!(-~7*D@Y6)^x6Rqo$cs z8;oP>iJ+GGGU<&z5;LfN`2`N-r*l>N>c;A{8-`Atuqp^F8Qjh78cm7DagD-CQT0LvgIr4}&oB z5df&T0sZ#s+=U#?J_%3hG53qN;e#K8j-0Sp5lRWxltfS)4&)z->X^z}45z`F!_*VO z_rY_!hCE!w<;#2<%1->QJE<-0{Zk3t521p`MF$%+>$xwNQ zCc=8Z?zoGk-0Al|tyhX?Y2wd5bve3J+;)Kks-r&70yLJG+-0#*#tWdFbmzL*#tg|B zVo_^dqUBkbX;T&Na?Ep6gq6Azq8guTqDc@+%sSuhW5HZ8j^>sSy=b;l5T6$Lp z)7uF;L=khUuA9ai*5J8uCW&C$Ab<+PTEw-qaP0yF0~}^U(kv?+A+1&mkuX+}N}<(B ze)Y2ie>ZF;R^Sn4Mpt77_}>(^HXnMLrNe-Xg^$)ytIH54^hYV} zq*8TolKWJs4GNmZd)46i(GbBYf*>dXV=Yk>5m!b!RyDrDzv~h5z@_^8k$u-cGfl8| zT%1EZ$eE)JPj<*|-oxG=Amr@vm<}GpmlLbe3bpfRr{%3}_)NvG|Km z=)V6R%q}{2nAP50Ha1-%@T*jhMg3u!(r2f^^PwSvQwB!{Qs#E!G_!Fc7*C83WKdZ( zVc*k@szneRU}WHFpTWazhU^j}L?~a$5kjdYg0tvovhDPsd7id__~ zc$Mzubw-^wq&Y*N)$%T~37JCUbz_t6&!oV6gR_B#2u>l!HU6+}qE=RUYtOPJ`NwMdi)QXZ`(8dW5FqZZ&ujG#0@W;G}}J7kM#{SQ=B%Iv}*du#{v#mcuY(cjF09 zzS#gWI`sTZu7P(CIW4&APcQa4_WQI5w-D?W@I)XaSR!CR%7}>h#kW}bPFuwgiPjbI1Too8DXJ>8!0l zqREwWfXQhrt#5E@(71-C9JLIh=fHSC`Lw%*CPA!$bf4k9e`M>MKVsP=!uUL)5jpsZ zg2}Oz3}HYt@wN#N`S`+VL9ky8R2Iv~(}0i=(dIFm^tj0SC-1>q7a-Q;+Sx^}tu)2J zqOzc@QgvbRv#tH_wX>+#9!%k5)8H9sh~Nz1MPJHk6#n2VsI7HGuz8oqpZ#|pefB%r z?9xUtBn1vozs2P#wfv5h8BhS8P9tuKl~LpcwK0Spuu!NK&-O}FqYjv-e&I~ZqjCx3(V7HM9v=I zQYp(83{H!#ihYfxM~bGL@HJ@(Eh)weu6{^_YY@bbrnDR~WS@+LE6m(jhjX(KM`Yr! z*$R1XiDPM`dxME+h@ipr!9xG;78#Fe@su7jv>0MAMIbnXBTXhk8fkUA7u|@rW#SmjiNJ%*W7Jt4@eEk9}&5=h=_`627g0UIC&-sZcWAaBFL=Da; z8X{;geXuYm36>s16df|+%I2IPG)v7sh%w_GDlwBqo57LD(K_6dDiK~CWb9vst0qoS z2nnFcQOtdC8FhIX<~j^T$gL$7*V^1XXWD2n^3+aqBaQ|yiiQXpOb^7Tkt#@TtYUeK z9rn17FiV0kgK&r?_b~z2gU5}q!53k|K0bl@wBK1TOEClB`QeMQnexAp5=Q&!Z?AE?&I0~!(>qgnPfgYW;3$QSUQIQ{9 z>-~C3 zOEIfAE|7e3mCZl=iP7ChjP5@{-#cLc_jk!L49FNTVn{{@OPd^M(Pg$E0*k?tBMea( zl-H+2JBPgTZ?$#BYrJY)oWm3q(~5+ghyyb0@_Ckj`Vm}QgQShkg-DbW^u}db>4}wx zKa6G5;Pjy(f(BCqH|y8=G4qpU z);|0(EY5={lxUw8>%-;BM-}{lqm!B#HhA;U5J7{ff>=Q$VlO*nAkfhf-I-Z>oi1{2 zktDY;5hnR#IMT-HC3KvjB z3yqL5!uYvoKa3c)KKSdFLtqy}%qojNxk9wQ>G)zxXws-2ro(2e7e6cJo{Izk9^fTMyX&{0koc z-#^mAV(23r(7`ZE!W?r58rUNv=YT%5EO}QDkh6ox=83da{oNHntqPUB+VbtHlsEpKFtU#9M z=x&SFS`U_2h*now*g0fj_mH<9e9hj2FBolXv;EBjwm-ka?w=mA!6p%Vyv;f0=#kPV zC1OPEbBhEarHmRty)pEwy>8{5S0f;2hPihyvGSMibMF1?aABUj6){pmWI*|1Fe{c+ zIRc2MY#334*N27(8cY=g&;tGa5jM5tNra9xD(+xajuDFy*gZI)l_W@~L()~y?!dYN z1KM0*zWo96_zCB}xzEnc+id^;TWs9C&FISw)He#vkRiLI2z%s2#Kg3TvE*zaK92A@ zt%2YcazX2PX5U}u!iP8D?KMm%Vj#|5(Mk~G?p3aY+^3EOH7-aRx%p(q8hX81a$^}*-lk8NoIVNArA3bnI3OV{8rZp|p5I?N-P+INNvGqBs%>}eBP@bJ z=#wy_!!GBrmo72?&JDP93F3(DK_A_oK}j@r1Y7L;g>G`=M0%qj#+b%fY%o2rq3Iem zc@+w!Z26G_;j@ATYd~_W_(l@O_*uRNEa)6~n#0{)`0gPG4>tJXx1X``%{}h@?pyl9 zFS*VZOB~Q9LNg@B78yoi_)Tya1uZ@xte47Xb!FMbbx&;pmJS0fIWdNa&*<eqCC_AXpGPj;?FzZ;{8QC6c7lm%-|5hno9B9mto%OtvLY6;#XG(^x~%HS(v zpUc@jRzwv9zK0P@E-qO#D_T;({77IggZ&{Kj2PT~#Ath;!S*il^bxoI+kYf}vdPwO z?=fPNghOAW06kJ>C{s=S85fp7=wlSL|LWAV7?`6Az#*1BQZ~pK;u_&?@fXOy{u_Su zt53Q5>H9=CF2Zt;vDIbjcu;3(5Y!;rTViJ%x{+z`Q=g2pw}V9H=$Kt5H> zg+ka>z8r*Ibrq1tLOh#5FM(WWFZO5;a^?mpr2EXiGe`g71G>NZj;)*Dad7hyySH}P z;gDJO8S$8yj2J@;%0BZ8vUoF9p)j71sidiI1QQn5q^ z#0VT_@MxQSYnQ!;TkL=S2X=q=d-BJ-jBf5=`a3B4M2u+ppt75;Z2a*o&Z*NTBjFJP zU=F=BkGe6>;)j=6{on@Y-@68vZ@~OTXeAIS(1MMHTv?35&l@O-po;N8bbVt(1aCST zB4}{NA@xu}C3VM$w+fW|*-Ewdkjn-VYf&PI6|{&i%u;9rVt~v*f9QJpd`d5jna-0eLA(&b>MU~5r+3gN+S;ad?*Z+*57>L~7`1gkYmk#g zmQ)!=l4Bx4+6kuH=E|oZ5HBpyTUsJnT%f(Qz^E0GYUR42$_R{`+uYKMSo!mB1b2h) zfyN?eaJDg?OIooALWfZ!ZYhk1BD=_?mKH;5j6or28-~CORtr`+jobn@&Bz8r5({l{ z1#ApbI2@4e?GqiQz9P(3d|73kRv&3hqLE%3S}mm2f+WF4ilH%#OzxakA}*?6g-Az4 z;_{PR4<@ON85p0Npe7N;k*=h#v%PkM(}IQw8oYV1QVXOOM1V`VRGtWe7_{DDq=MVU z8mw3{fm{o@pJL!zP`LlJ1#LhXRpjoA4_KFja6HOFheD8Be@+%^!oBi^BUV5$d`4W0 zI;kFmrt4^f|7#r2kOZg?N2*&k;9!uwx;yYr) z@#@DY&aybh*op=XULqPIXz<3tj@2U6M5_-E1ns#xyEw?h#PHR6^Jf--}yC|cGTlwN)D zMPkZl5*fycAbhSoC-FWev7z{@8lk8h&wsht&$BM;HKlx%_xWYqG+Lwr{1+ELLQgo}zBAy-0SnyZ?Rgvn2Fgp?zmRb-xY(3KXYbxXfPW zo`c{D(y@%ZSggg$t2-w^@*TyT3;)Hxbm7h0tkU+ia>Mm z;#D=jO7>7K0jNv~s?*I23bwM@T(RcQ=LXMe)(^bT#U;@yahpQFq@ZPv-^=Hpgyvmp z*81yp65R^qJ_**ySj;2r+uBmXo)T2=RnBqk$o;_836_EJ#8*v55bG2PWdT2duWBWV z3ESL~M=(_7F(V5Ssst>P9+ZX%8k{|3Syo%X5kVk>faA2QPN#!bnmo@j#<=e(1r}d1 zP(_6V1gN=toZ2^TO@tW4QCWF(&5MY*m+ulQb<-f!z&EcObJx_)<0KNOZFgoy`5h+( z3r{kyfl00000NkvXXu0mjf3PI;; literal 0 HcmV?d00001 diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 000000000..6e3480a08 --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,7 @@ +.app { + min-height: 100vh; +} + +.home { + padding: 2rem 1.5rem; +} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx new file mode 100644 index 000000000..07a40b2e8 --- /dev/null +++ b/frontend/src/App.jsx @@ -0,0 +1,17 @@ +import './App.css' +import Navbar from './components/Navbar' +import Home from './pages/Home' + +// Root component. For now it renders the Navbar + Home page directly. +// TODO: once we add more pages, introduce React Router here +// (npm install react-router-dom) and swap for . +function App() { + return ( +
+ + +
+ ) +} + +export default App; \ No newline at end of file diff --git a/frontend/src/api/client.js b/frontend/src/api/client.js new file mode 100644 index 000000000..2104bb4c2 --- /dev/null +++ b/frontend/src/api/client.js @@ -0,0 +1,27 @@ +// Central API client — a thin wrapper around fetch. +// All backend calls should go through here so we configure the base URL, +// headers, and error handling in one place. +// +// Vite reads env vars prefixed with VITE_ (see .env). Falls back to the +// local backend if VITE_API_URL is not set. +const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000/api' + +async function request(path, options = {}) { + const res = await fetch(`${BASE_URL}${path}`, { + headers: { 'Content-Type': 'application/json' }, + ...options, + }) + + if (!res.ok) { + throw new Error(`API error ${res.status}: ${res.statusText}`) + } + + return res.json() +} + +export const api = { + get: (path) => request(path), + post: (path, body) => request(path, { method: 'POST', body: JSON.stringify(body) }), + put: (path, body) => request(path, { method: 'PUT', body: JSON.stringify(body) }), + del: (path) => request(path, { method: 'DELETE' }), +} diff --git a/frontend/src/components/Navbar.css b/frontend/src/components/Navbar.css new file mode 100644 index 000000000..1b442c824 --- /dev/null +++ b/frontend/src/components/Navbar.css @@ -0,0 +1,11 @@ +.navbar { + display: flex; + align-items: center; + padding: 1rem 1.5rem; + border-bottom: 1px solid #eee; +} + +.navbar__logo { + font-size: 1.25rem; + font-weight: 700; +} diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx new file mode 100644 index 000000000..4724de66f --- /dev/null +++ b/frontend/src/components/Navbar.jsx @@ -0,0 +1,13 @@ +import './Navbar.css' + +// Top navigation bar. Shown on every page. +// TODO: add nav links, login/profile button, mobile menu. +function Navbar() { + return ( + + ) +} + +export default Navbar diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 000000000..e9e0a896d --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,16 @@ +*, +*::before, +*::after { + box-sizing: border-box; +} + +* { + margin: 0; +} + +body { + min-height: 100vh; + font-family: system-ui, Avenir, Helvetica, Arial, sans-serif; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx new file mode 100644 index 000000000..b9a1a6dea --- /dev/null +++ b/frontend/src/main.jsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.jsx' + +createRoot(document.getElementById('root')).render( + + + , +) diff --git a/frontend/src/pages/Home.jsx b/frontend/src/pages/Home.jsx new file mode 100644 index 000000000..ff2f42b8a --- /dev/null +++ b/frontend/src/pages/Home.jsx @@ -0,0 +1,12 @@ +// Home / landing page. +// TODO: fetch venues from the API (see src/api/client.js) and +// render them as a list of cards. +function Home() { + return ( +
+

Find & Book the Perfect Space Near You

+
+ ) +} + +export default Home diff --git a/frontend/vite.config.js b/frontend/vite.config.js new file mode 100644 index 000000000..8b0f57b91 --- /dev/null +++ b/frontend/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) From f10b946b687dcd86d93aa5efb21c0130338dde34 Mon Sep 17 00:00:00 2001 From: Akhin Date: Sat, 30 May 2026 19:30:14 +0530 Subject: [PATCH 02/93] tailwind css setup --- frontend/package-lock.json | 368 +++++++++++++++++++++++++---- frontend/package.json | 4 +- frontend/src/App.css | 7 - frontend/src/App.jsx | 3 +- frontend/src/components/Navbar.css | 11 - frontend/src/components/Navbar.jsx | 6 +- frontend/src/index.css | 12 +- frontend/src/pages/Home.jsx | 4 +- frontend/vite.config.js | 3 +- 9 files changed, 327 insertions(+), 91 deletions(-) delete mode 100644 frontend/src/App.css delete mode 100644 frontend/src/components/Navbar.css diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d5afc4a97..1bcd2a6ab 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,8 +8,10 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@tailwindcss/vite": "^4.3.0", "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "tailwindcss": "^4.3.0" }, "devDependencies": { "@eslint/js": "^10.0.1", @@ -267,7 +269,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -279,7 +280,6 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -290,7 +290,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -495,7 +494,6 @@ "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", @@ -506,7 +504,6 @@ "version": "2.3.5", "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", @@ -517,7 +514,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -527,14 +523,12 @@ "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -545,7 +539,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -564,7 +557,6 @@ "version": "0.132.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.132.0.tgz", "integrity": "sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -577,7 +569,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -594,7 +585,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -611,7 +601,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -628,7 +617,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -645,7 +633,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -662,7 +649,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -679,7 +665,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -696,7 +681,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -713,7 +697,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -730,7 +713,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -747,7 +729,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -764,7 +745,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -781,7 +761,6 @@ "cpu": [ "wasm32" ], - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -800,7 +779,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -817,7 +795,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -831,14 +808,269 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", - "dev": true, "license": "MIT" }, + "node_modules/@tailwindcss/node": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.0.tgz", + "integrity": "sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.21.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.0.tgz", + "integrity": "sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-arm64": "4.3.0", + "@tailwindcss/oxide-darwin-x64": "4.3.0", + "@tailwindcss/oxide-freebsd-x64": "4.3.0", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.0", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.0", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.0", + "@tailwindcss/oxide-linux-x64-musl": "4.3.0", + "@tailwindcss/oxide-wasm32-wasi": "4.3.0", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.0", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.0" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.0.tgz", + "integrity": "sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.0.tgz", + "integrity": "sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.0.tgz", + "integrity": "sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.0.tgz", + "integrity": "sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.0.tgz", + "integrity": "sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.0.tgz", + "integrity": "sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.0.tgz", + "integrity": "sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.0.tgz", + "integrity": "sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.0.tgz", + "integrity": "sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.0.tgz", + "integrity": "sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.10.0", + "@emnapi/runtime": "^1.10.0", + "@emnapi/wasi-threads": "^1.2.1", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.0.tgz", + "integrity": "sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.0.tgz", + "integrity": "sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.0.tgz", + "integrity": "sha512-t6J3OrB5Fc0ExuhohouH0fWUGMYL6PTLhW+E7zIk/pdbnJARZDCwjBznFnkh5ynRnIRSI4YjtTH0t6USjJISrw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.0", + "@tailwindcss/oxide": "4.3.0", + "tailwindcss": "4.3.0" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, "node_modules/@tybys/wasm-util": { "version": "0.10.2", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", - "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -1101,7 +1333,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "dev": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -1114,6 +1345,19 @@ "dev": true, "license": "ISC" }, + "node_modules/enhanced-resolve": { + "version": "5.22.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.1.tgz", + "integrity": "sha512-6QEuw3zoX1SJQc7b87aBXke/no+mG2bTBgw29gWMQonLmpEkWoCAVkl+M49e48AZlWzxiDzDZzYdp6kobcyLww==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1344,7 +1588,6 @@ "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" @@ -1413,7 +1656,6 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -1460,6 +1702,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -1527,6 +1775,15 @@ "dev": true, "license": "ISC" }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -1609,7 +1866,6 @@ "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", - "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" @@ -1642,7 +1898,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1663,7 +1918,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1684,7 +1938,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1705,7 +1958,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1726,7 +1978,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1747,7 +1998,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1768,7 +2018,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1789,7 +2038,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1810,7 +2058,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1831,7 +2078,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1852,7 +2098,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MPL-2.0", "optional": true, "os": [ @@ -1892,6 +2137,15 @@ "yallist": "^3.0.2" } }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -1919,7 +2173,6 @@ "version": "3.3.12", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", - "dev": true, "funding": [ { "type": "github", @@ -2025,14 +2278,12 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -2045,7 +2296,6 @@ "version": "8.5.15", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.15.tgz", "integrity": "sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -2115,7 +2365,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", - "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.132.0", @@ -2188,17 +2437,34 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", - "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -2215,7 +2481,6 @@ "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", - "dev": true, "license": "0BSD", "optional": true }, @@ -2277,7 +2542,6 @@ "version": "8.0.14", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", - "dev": true, "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", diff --git a/frontend/package.json b/frontend/package.json index e4d03ce24..edb891601 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,8 +10,10 @@ "preview": "vite preview" }, "dependencies": { + "@tailwindcss/vite": "^4.3.0", "react": "^19.2.6", - "react-dom": "^19.2.6" + "react-dom": "^19.2.6", + "tailwindcss": "^4.3.0" }, "devDependencies": { "@eslint/js": "^10.0.1", diff --git a/frontend/src/App.css b/frontend/src/App.css deleted file mode 100644 index 6e3480a08..000000000 --- a/frontend/src/App.css +++ /dev/null @@ -1,7 +0,0 @@ -.app { - min-height: 100vh; -} - -.home { - padding: 2rem 1.5rem; -} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 07a40b2e8..866469b01 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,4 +1,3 @@ -import './App.css' import Navbar from './components/Navbar' import Home from './pages/Home' @@ -7,7 +6,7 @@ import Home from './pages/Home' // (npm install react-router-dom) and swap for . function App() { return ( -
+
diff --git a/frontend/src/components/Navbar.css b/frontend/src/components/Navbar.css deleted file mode 100644 index 1b442c824..000000000 --- a/frontend/src/components/Navbar.css +++ /dev/null @@ -1,11 +0,0 @@ -.navbar { - display: flex; - align-items: center; - padding: 1rem 1.5rem; - border-bottom: 1px solid #eee; -} - -.navbar__logo { - font-size: 1.25rem; - font-weight: 700; -} diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 4724de66f..5ba1fe10c 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -1,11 +1,9 @@ -import './Navbar.css' - // Top navigation bar. Shown on every page. // TODO: add nav links, login/profile button, mobile menu. function Navbar() { return ( -
+ } /> + + + + + } /> + + } /> +
+ ) } From 702f410aeae311e52f21c979b8d3aff6371dcc80 Mon Sep 17 00:00:00 2001 From: "Arjun.O" Date: Wed, 3 Jun 2026 12:47:12 +0530 Subject: [PATCH 13/93] chore: Added Admin Login and Home Page, Set Routing --- frontend/src/App.jsx | 9 +++++- frontend/src/components/AdminNavbar.jsx | 36 +++++++++++++++++++++ frontend/src/pages/AdminPage/AdminHome.jsx | 7 ++++ frontend/src/pages/AdminPage/AdminLogin.jsx | 35 ++++++++++++++++++-- frontend/src/pages/AdminPage/AdminPages.js | 4 ++- 5 files changed, 86 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/AdminNavbar.jsx create mode 100644 frontend/src/pages/AdminPage/AdminHome.jsx diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index cb3b0e150..47a84478e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -1,8 +1,9 @@ import { Routes, Route } from 'react-router-dom'; import Navbar from './components/Navbar'; +import AdminNavbar from './components/AdminNavbar.jsx' import { Home, Venue, Category, Signin } from './pages/UserPages/UserPages.js'; -import { AdminLogin } from './pages/AdminPage/AdminPages.js'; +import { AdminLogin,AdminHome } from './pages/AdminPage/AdminPages.js'; function App() { return ( @@ -36,6 +37,12 @@ function App() { } /> } /> + + + + + } />
) diff --git a/frontend/src/components/AdminNavbar.jsx b/frontend/src/components/AdminNavbar.jsx new file mode 100644 index 000000000..56bbbe6cc --- /dev/null +++ b/frontend/src/components/AdminNavbar.jsx @@ -0,0 +1,36 @@ +import { Link } from "react-router-dom"; + +export default function Navbar() { + + return ( + + + ) +} + diff --git a/frontend/src/pages/AdminPage/AdminHome.jsx b/frontend/src/pages/AdminPage/AdminHome.jsx new file mode 100644 index 000000000..7645052d9 --- /dev/null +++ b/frontend/src/pages/AdminPage/AdminHome.jsx @@ -0,0 +1,7 @@ +export function AdminHome() { + return ( +
+

Admin Home Page

+
+ ) +} diff --git a/frontend/src/pages/AdminPage/AdminLogin.jsx b/frontend/src/pages/AdminPage/AdminLogin.jsx index 7973dde60..ad32543bd 100644 --- a/frontend/src/pages/AdminPage/AdminLogin.jsx +++ b/frontend/src/pages/AdminPage/AdminLogin.jsx @@ -1,5 +1,34 @@ -export function AdminLogin(){ - return( -

Admin Page

+import { Link } from "react-router-dom" +export function AdminLogin() { + return ( +
+ +
+ +

+ Admin Login +

+ + + + + + + + + +
+ +
) } \ No newline at end of file diff --git a/frontend/src/pages/AdminPage/AdminPages.js b/frontend/src/pages/AdminPage/AdminPages.js index df143b9d6..69a8bd08f 100644 --- a/frontend/src/pages/AdminPage/AdminPages.js +++ b/frontend/src/pages/AdminPage/AdminPages.js @@ -1,5 +1,7 @@ -import { AdminLogin } from "./AdminLogin"; +import { AdminLogin } from "./AdminLogin.jsx"; +import { AdminHome } from "./AdminHome.jsx" export { AdminLogin, + AdminHome, } \ No newline at end of file From 9f88080831905cebac9ada845f9d41e41928c5e4 Mon Sep 17 00:00:00 2001 From: "Arjun.O" Date: Wed, 3 Jun 2026 13:22:55 +0530 Subject: [PATCH 14/93] chore: Added Venue Owner Home and Login Pages, Routing --- frontend/src/App.jsx | 13 +++++++ frontend/src/components/VenueOwnerNavbar.jsx | 36 +++++++++++++++++++ .../src/pages/OwnerPage/VenueOwnerHome.jsx | 7 ++++ .../src/pages/OwnerPage/VenueOwnerLogin.jsx | 34 ++++++++++++++++++ .../src/pages/OwnerPage/VenueOwnerPage.js | 7 ++++ 5 files changed, 97 insertions(+) create mode 100644 frontend/src/components/VenueOwnerNavbar.jsx create mode 100644 frontend/src/pages/OwnerPage/VenueOwnerHome.jsx create mode 100644 frontend/src/pages/OwnerPage/VenueOwnerLogin.jsx create mode 100644 frontend/src/pages/OwnerPage/VenueOwnerPage.js diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 47a84478e..cf3936553 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -2,8 +2,10 @@ import { Routes, Route } from 'react-router-dom'; import Navbar from './components/Navbar'; import AdminNavbar from './components/AdminNavbar.jsx' +import VenueOwnerNavbar from './components/VenueOwnerNavbar.jsx' import { Home, Venue, Category, Signin } from './pages/UserPages/UserPages.js'; import { AdminLogin,AdminHome } from './pages/AdminPage/AdminPages.js'; +import {VenueOwnerHome,VenueOwnerLogin} from './pages/OwnerPage/VenueOwnerPage.js' function App() { return ( @@ -36,6 +38,17 @@ function App() { } /> + + {/* Venue Owner Routings */} + } /> + + + + + } /> + + {/* Admin Routings */} } /> diff --git a/frontend/src/components/VenueOwnerNavbar.jsx b/frontend/src/components/VenueOwnerNavbar.jsx new file mode 100644 index 000000000..04c128f63 --- /dev/null +++ b/frontend/src/components/VenueOwnerNavbar.jsx @@ -0,0 +1,36 @@ +import { Link } from "react-router-dom"; + +export default function VenueOwnerNavbar() { + + return ( + + + ) +} + diff --git a/frontend/src/pages/OwnerPage/VenueOwnerHome.jsx b/frontend/src/pages/OwnerPage/VenueOwnerHome.jsx new file mode 100644 index 000000000..dd3940132 --- /dev/null +++ b/frontend/src/pages/OwnerPage/VenueOwnerHome.jsx @@ -0,0 +1,7 @@ +export function VenueOwnerHome() { + return ( +
+

Venue Owner Home Page

+
+ ) +} diff --git a/frontend/src/pages/OwnerPage/VenueOwnerLogin.jsx b/frontend/src/pages/OwnerPage/VenueOwnerLogin.jsx new file mode 100644 index 000000000..a8d1d0d30 --- /dev/null +++ b/frontend/src/pages/OwnerPage/VenueOwnerLogin.jsx @@ -0,0 +1,34 @@ +import { Link } from "react-router-dom" +export function VenueOwnerLogin() { + return ( +
+ +
+ +

+ Venue Owner Login +

+ + + + + + + + + +
+ +
+ ) +} \ No newline at end of file diff --git a/frontend/src/pages/OwnerPage/VenueOwnerPage.js b/frontend/src/pages/OwnerPage/VenueOwnerPage.js new file mode 100644 index 000000000..e0d186741 --- /dev/null +++ b/frontend/src/pages/OwnerPage/VenueOwnerPage.js @@ -0,0 +1,7 @@ +import {VenueOwnerHome} from './VenueOwnerHome.jsx'; +import {VenueOwnerLogin} from './VenueOwnerLogin.jsx'; + +export { + VenueOwnerHome, + VenueOwnerLogin, +} \ No newline at end of file From a2a235acd4190893698684135ac198cdbe9a84bd Mon Sep 17 00:00:00 2001 From: "Arjun.O" Date: Wed, 3 Jun 2026 15:42:52 +0530 Subject: [PATCH 15/93] feat: Established API Connections, And Display Category Page --- frontend/src/components/CategoryCard.jsx | 39 +++++++++++ frontend/src/pages/UserPages/Category.jsx | 66 +++++++++++++++++-- .../src/services/venueCategory.service.js | 6 ++ 3 files changed, 107 insertions(+), 4 deletions(-) create mode 100644 frontend/src/components/CategoryCard.jsx create mode 100644 frontend/src/services/venueCategory.service.js diff --git a/frontend/src/components/CategoryCard.jsx b/frontend/src/components/CategoryCard.jsx new file mode 100644 index 000000000..cd924fd2b --- /dev/null +++ b/frontend/src/components/CategoryCard.jsx @@ -0,0 +1,39 @@ +export default function CategoryCard({ category, bgColor }) { + return ( +
+

+ {category.name} +

+
+ ); +} + diff --git a/frontend/src/pages/UserPages/Category.jsx b/frontend/src/pages/UserPages/Category.jsx index 96c26b1b6..f70b72809 100644 --- a/frontend/src/pages/UserPages/Category.jsx +++ b/frontend/src/pages/UserPages/Category.jsx @@ -1,6 +1,64 @@ -// TODO: List All Categories , Redirect To Venue with The category as filter. -export function Category(){ +import { useEffect, useState } from "react"; +import { getVenueCategories } from "../../services/venueCategory.service.js"; +import CategoryCard from "../../components/CategoryCard.jsx"; + +export function Category() { + const [categories, setCategories] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(""); + + const bgColors = [ + "hover:bg-sky-50", + "hover:bg-amber-50", + "hover:bg-emerald-50", + ]; + + useEffect(() => { + const fetchCategories = async () => { + try { + const data = await getVenueCategories(); + setCategories(data); + } catch (err) { + setError(err.message); + } finally { + setLoading(false); + } + }; + + fetchCategories(); + }, []); + + if (loading) { return ( -

This is Categories Page

- ) +
+ Loading categories... +
+ ); + } + + if (error) { + return ( +
+ {error} +
+ ); + } + + return ( +
+

+ Venue Categories +

+ +
+ {categories.map((category, index) => ( + + ))} +
+
+ ); } \ No newline at end of file diff --git a/frontend/src/services/venueCategory.service.js b/frontend/src/services/venueCategory.service.js new file mode 100644 index 000000000..a09ede8f3 --- /dev/null +++ b/frontend/src/services/venueCategory.service.js @@ -0,0 +1,6 @@ +import {api} from '../api/client.js'; + +export const getVenueCategories = async () => { + const response = await api.get("/venueCategories"); + return response.data; +} \ No newline at end of file From 41686baec16fde534f0066c881138712d6e4f2be Mon Sep 17 00:00:00 2001 From: "Arjun.O" Date: Wed, 3 Jun 2026 15:43:46 +0530 Subject: [PATCH 16/93] fix: CORS and /api EndPoint Fixed --- backend/index.js | 6 ++++-- backend/package-lock.json | 27 +++++++++++++++++++++++++++ backend/package.json | 1 + 3 files changed, 32 insertions(+), 2 deletions(-) diff --git a/backend/index.js b/backend/index.js index 2e1ab1049..8ad258a9a 100644 --- a/backend/index.js +++ b/backend/index.js @@ -3,15 +3,17 @@ const express = require("express"); const { connectDB } = require("./config/database"); const venueRoutes = require("./routes/venue"); const categoryRoutes = require("./routes/category"); +const cors = require("cors"); const PORT = process.env.PORT || 8000; const app = express(); +app.use(cors()) app.use(express.json()); -app.use("/venues", venueRoutes); -app.use("/venueCategories", categoryRoutes); +app.use("/api/venues", venueRoutes); +app.use("/api/venueCategories", categoryRoutes); connectDB() .then(() => { diff --git a/backend/package-lock.json b/backend/package-lock.json index dcbcb1b57..2375f0d30 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,6 +9,7 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "cors": "^2.8.6", "dotenv": "^17.4.2", "express": "^5.2.1", "mongoose": "^9.6.3" @@ -162,6 +163,23 @@ "node": ">=6.6.0" } }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -678,6 +696,15 @@ "node": ">= 0.6" } }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", diff --git a/backend/package.json b/backend/package.json index efa0f75ea..5cf888263 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,6 +10,7 @@ "author": "Team of people", "license": "ISC", "dependencies": { + "cors": "^2.8.6", "dotenv": "^17.4.2", "express": "^5.2.1", "mongoose": "^9.6.3" From 91f5c9180fda2e47a2cc547ec71a5fd8e20c8da3 Mon Sep 17 00:00:00 2001 From: "Arjun.O" Date: Wed, 3 Jun 2026 16:05:05 +0530 Subject: [PATCH 17/93] feat: Added Venue Page Cards And API responses --- frontend/src/components/VenueCard.jsx | 43 ++++++++++++++++++++++++ frontend/src/pages/UserPages/Venue.jsx | 45 +++++++++++++++++++++++--- frontend/src/services/venue.service.js | 6 ++++ 3 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 frontend/src/components/VenueCard.jsx create mode 100644 frontend/src/services/venue.service.js diff --git a/frontend/src/components/VenueCard.jsx b/frontend/src/components/VenueCard.jsx new file mode 100644 index 000000000..e584a9186 --- /dev/null +++ b/frontend/src/components/VenueCard.jsx @@ -0,0 +1,43 @@ +function VenueCard({ venue }) { + return ( +
+ + {venue.name} + +
+ + {venue.venueCategory.name} + + +

+ {venue.name} +

+ +

+ {venue.description} +

+ +
+

📍 {venue.city}, {venue.state}

+

👥 Capacity: {venue.capacity}

+
+ +
+ + ₹{venue.basePrice.toLocaleString()} + + + +
+
+
+ ); +} + +export default VenueCard; \ No newline at end of file diff --git a/frontend/src/pages/UserPages/Venue.jsx b/frontend/src/pages/UserPages/Venue.jsx index fd08e8a73..2dae5519d 100644 --- a/frontend/src/pages/UserPages/Venue.jsx +++ b/frontend/src/pages/UserPages/Venue.jsx @@ -1,6 +1,41 @@ -// TODO: List Venues Using API. -export function Venue(){ - return ( -

This is Venue Page

- ) +import { useEffect, useState } from "react"; +import { getVenues } from "../../services/venue.service.js"; +import VenueCard from "../../components/VenueCard.jsx"; + +export function Venue() { + const [venues, setVenues] = useState([]); + + useEffect(() => { + const loadVenues = async () => { + try { + const data = await getVenues(); + setVenues(data); + } catch (error) { + console.error(error); + } + }; + + loadVenues(); + }, []); + + return ( +
+ + {/* Heading */} +

+ Venues +

+ + {/* Cards Grid */} +
+ {venues.map((venue) => ( + + ))} +
+ +
+ ); } \ No newline at end of file diff --git a/frontend/src/services/venue.service.js b/frontend/src/services/venue.service.js new file mode 100644 index 000000000..0384633f9 --- /dev/null +++ b/frontend/src/services/venue.service.js @@ -0,0 +1,6 @@ +import { api } from "../api/client.js"; + +export const getVenues = async () => { + const response = await api.get("/venues"); + return response.data; +} \ No newline at end of file From 6a22925d93010cac92dcbf601418deaa0298e25f Mon Sep 17 00:00:00 2001 From: "Arjun.O" Date: Wed, 3 Jun 2026 16:16:27 +0530 Subject: [PATCH 18/93] feat:Added Pagination --- frontend/src/pages/UserPages/Venue.jsx | 53 +++++++++++++++++++++++--- frontend/src/services/venue.service.js | 7 ++-- 2 files changed, 51 insertions(+), 9 deletions(-) diff --git a/frontend/src/pages/UserPages/Venue.jsx b/frontend/src/pages/UserPages/Venue.jsx index 2dae5519d..626f8b096 100644 --- a/frontend/src/pages/UserPages/Venue.jsx +++ b/frontend/src/pages/UserPages/Venue.jsx @@ -4,30 +4,49 @@ import VenueCard from "../../components/VenueCard.jsx"; export function Venue() { const [venues, setVenues] = useState([]); + const [page, setPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [loading, setLoading] = useState(false); useEffect(() => { const loadVenues = async () => { try { - const data = await getVenues(); - setVenues(data); + setLoading(true); + + const response = await getVenues(page); + + setVenues(response.data); + setTotalPages(response.pagination.totalPages); + } catch (error) { console.error(error); + } finally { + setLoading(false); } }; loadVenues(); - }, []); + }, [page]); + + if (loading) { + return ( +
+

Venues

+

Loading venues...

+
+ ); +} return (
- + {/* Heading */}

Venues

{/* Cards Grid */} -
+
{venues.map((venue) => ( +
+ + + + + Page {page} of {totalPages} + + + + +
+
); } \ No newline at end of file diff --git a/frontend/src/services/venue.service.js b/frontend/src/services/venue.service.js index 0384633f9..e3a328747 100644 --- a/frontend/src/services/venue.service.js +++ b/frontend/src/services/venue.service.js @@ -1,6 +1,5 @@ import { api } from "../api/client.js"; -export const getVenues = async () => { - const response = await api.get("/venues"); - return response.data; -} \ No newline at end of file +export const getVenues = async (page = 1) => { + return await api.get(`/venues?page=${page}`); +}; \ No newline at end of file From 9021122d437edab60c54f25d7c45c501c62fa7dd Mon Sep 17 00:00:00 2001 From: "Arjun.O" Date: Thu, 4 Jun 2026 21:57:00 +0530 Subject: [PATCH 19/93] wip: Working On Landing Page --- frontend/src/components/HomeVenueCard.jsx | 37 +++++++ frontend/src/components/HomeVenueSection.jsx | 27 +++++ frontend/src/pages/UserPages/Home.jsx | 101 +++++++++++++++++-- 3 files changed, 159 insertions(+), 6 deletions(-) create mode 100644 frontend/src/components/HomeVenueCard.jsx create mode 100644 frontend/src/components/HomeVenueSection.jsx diff --git a/frontend/src/components/HomeVenueCard.jsx b/frontend/src/components/HomeVenueCard.jsx new file mode 100644 index 000000000..f04fa3fac --- /dev/null +++ b/frontend/src/components/HomeVenueCard.jsx @@ -0,0 +1,37 @@ +export default function HomeVenueCard({ venue }) { + return ( +
+ + {venue.name} + +
+ +

+ {venue.name} +

+ +

+ {venue.venueCategory.name} +

+ +

+ 📍 {venue.district} +

+ +

+ 👥 {venue.capacity} Guests +

+ +

+ ₹{venue.basePrice.toLocaleString()} +

+ +
+ +
+ ); +} \ No newline at end of file diff --git a/frontend/src/components/HomeVenueSection.jsx b/frontend/src/components/HomeVenueSection.jsx new file mode 100644 index 000000000..ed5e21140 --- /dev/null +++ b/frontend/src/components/HomeVenueSection.jsx @@ -0,0 +1,27 @@ +import VenueCard from "./HomeVenueCard.jsx"; + +export function HomeVenueSection({ + title, + venues, +}) { + return ( +
+ +

+ {title} +

+ +
+ + {venues.map((venue) => ( + + ))} + +
+ +
+ ); +} \ No newline at end of file diff --git a/frontend/src/pages/UserPages/Home.jsx b/frontend/src/pages/UserPages/Home.jsx index a76916312..609dae0f0 100644 --- a/frontend/src/pages/UserPages/Home.jsx +++ b/frontend/src/pages/UserPages/Home.jsx @@ -1,10 +1,99 @@ -// Home / landing page. -// TODO: fetch venues from the API (see src/api/client.js) and -// render them as a list of cards. +import { useEffect, useMemo, useState } from "react"; +import { getVenues } from "../../services/venue.service.js"; +import { getVenueCategories } from "../../services/venueCategory.service.js"; +import { HomeVenueSection } from "../../components/HomeVenueSection.jsx"; + +function getRandomCategories(categories, count = 2) { + const shuffled = [...categories].sort(() => Math.random() - 0.5); + return shuffled.slice(0, count); +} + export function Home() { + const [venues, setVenues] = useState([]); + const [categories, setCategories] = useState([]); + + const selectedCategories = useMemo(() => { + if (!categories.length) return []; + + const stored = sessionStorage.getItem("homeCategories"); + + if (stored) { + return JSON.parse(stored); + } + + const categoryNames = categories.map( + (category) => category.name + ); + + const randomCategories = + getRandomCategories(categoryNames); + + sessionStorage.setItem( + "homeCategories", + JSON.stringify(randomCategories) + ); + + return randomCategories; + }, [categories]); + + useEffect(() => { + async function loadData() { + try { + const venueResponse = await getVenues(); + const categoryResponse = await getVenueCategories(); + + setVenues(venueResponse.data); + setCategories(categoryResponse.data); + } catch (error) { + console.error(error); + } + } + + loadData(); + }, []); + + return (
-

Find & Book the Perfect Space Near You

+ + {/* Hero Banner */} + + + + a.basePrice - b.basePrice) + .slice(0, 4)} + /> + + {selectedCategories[0] && ( + + venue.venueCategory?.name === selectedCategories[0] + ) + .slice(0, 4)} + /> + )} + + {selectedCategories[1] && ( + + venue.venueCategory?.name === selectedCategories[1] + ) + .slice(0, 4)} + /> + )} +
- ) -} + ); +} \ No newline at end of file From fed6106876b371910cdd7c0676d28b83e307b6ee Mon Sep 17 00:00:00 2001 From: Akhin Date: Thu, 4 Jun 2026 22:17:34 +0530 Subject: [PATCH 20/93] feat(backend): add Google OAuth login and JWT authentication --- backend/config/config.js | 13 +- backend/constants/user.js | 16 + backend/controllers/auth/getMe.js | 22 ++ backend/controllers/auth/googleLogin.js | 75 +++++ backend/controllers/auth/shared.js | 23 ++ backend/index.js | 6 + backend/middleware/authenticate.js | 37 +++ backend/models/user.js | 33 ++ backend/package-lock.json | 388 ++++++++++++++++++++++++ backend/package.json | 3 + backend/routes/auth.js | 11 + backend/utils/jwt.js | 17 ++ 12 files changed, 643 insertions(+), 1 deletion(-) create mode 100644 backend/constants/user.js create mode 100644 backend/controllers/auth/getMe.js create mode 100644 backend/controllers/auth/googleLogin.js create mode 100644 backend/controllers/auth/shared.js create mode 100644 backend/middleware/authenticate.js create mode 100644 backend/models/user.js create mode 100644 backend/routes/auth.js create mode 100644 backend/utils/jwt.js diff --git a/backend/config/config.js b/backend/config/config.js index 7571ed1c8..e8ca89953 100644 --- a/backend/config/config.js +++ b/backend/config/config.js @@ -1,3 +1,14 @@ module.exports = { mongodbConnectionUrl: process.env.MONGODB_CONNECTION_URL, -}; \ No newline at end of file + + // Google OAuth — the Web client ID from Google Cloud Console. + // Used to verify that an incoming Google ID token was issued for THIS app. + googleClientId: process.env.GOOGLE_CLIENT_ID, + + jwtSecret: process.env.JWT_SECRET, + jwtExpiresIn: "7d", + + // Comma-separated list of frontend origins allowed by CORS. + // Defaults to the Vite dev server. + corsOrigin: process.env.CORS_ORIGIN || "http://localhost:5173", +}; diff --git a/backend/constants/user.js b/backend/constants/user.js new file mode 100644 index 000000000..9f6cb0c63 --- /dev/null +++ b/backend/constants/user.js @@ -0,0 +1,16 @@ +const USER_ROLES = Object.freeze({ + CUSTOMER: "customer", // customer books venues (default for new sign-ups from user sign up tab) + VENUE_OWNER: "venueOwner", // lists and manages venues + ADMIN: "admin", // platform administration +}); + +const USER_ROLE_VALUES = Object.values(USER_ROLES); + +// Roles a user may pick for themselves when signing up via the public tabs. +// "admin" is deliberately excluded — it can never be self-assigned. +const SELF_SIGNUP_ROLES = Object.freeze([ + USER_ROLES.CUSTOMER, + USER_ROLES.VENUE_OWNER, +]); + +module.exports = { USER_ROLES, USER_ROLE_VALUES, SELF_SIGNUP_ROLES }; diff --git a/backend/controllers/auth/getMe.js b/backend/controllers/auth/getMe.js new file mode 100644 index 000000000..37b73fe1b --- /dev/null +++ b/backend/controllers/auth/getMe.js @@ -0,0 +1,22 @@ +// GET /auth/me — returns the currently authenticated user. +// authenticate has already verified the token and attached req.user. +async function getMe(req, res) { + try { + const { _id, email, name, picture, role } = req.user; + const user = { + id: _id.toString(), + email, + name, + picture, + role, + }; + + return res.status(200).json({ data: { user } }); + } catch (err) { + return res + .status(500) + .json({ error: err.message, message: "Failed to fetch user" }); + } +} + +module.exports = getMe; diff --git a/backend/controllers/auth/googleLogin.js b/backend/controllers/auth/googleLogin.js new file mode 100644 index 000000000..37f966377 --- /dev/null +++ b/backend/controllers/auth/googleLogin.js @@ -0,0 +1,75 @@ +const Users = require("../../models/user"); +const { USER_ROLES, SELF_SIGNUP_ROLES } = require("../../constants/user"); +const { signAuthToken } = require("../../utils/jwt"); +const { verifyGoogleIdToken } = require("./shared"); + +// Human-readable tab name for each self-signup role, used in error messages. +const ROLE_TAB_LABEL = { + [USER_ROLES.CUSTOMER]: "User", + [USER_ROLES.VENUE_OWNER]: "Venue Owner", +}; + +// POST /auth/googleLogin +// Body: { idToken, role } — the Google ID token from the frontend, plus the +// role the chosen tab represents ("customer" or "venueOwner"). +// Verifies the token, creates the user with that role (or enforces their +// existing role), and returns our own app JWT. +async function googleLogin(req, res) { + try { + const { idToken, role } = req.body || {}; + if (!idToken) { + return res.status(400).json({ message: "idToken is required" }); + } + if (!SELF_SIGNUP_ROLES.includes(role)) { + return res.status(400).json({ message: "A valid role is required" }); + } + + // 1. Verify the token really came from Google and is meant for us(this application). + let profile; + try { + profile = await verifyGoogleIdToken(idToken); + } catch (err) { + return res + .status(401) + .json({ error: err.message, message: "Invalid Google token" }); + } + + // 2. Email is the primary identity key — find the existing user by it. + let user = await Users.findOne({ email: profile.email }); + + if (user) { + // An existing account's role is fixed. If they signed in via the wrong + // tab, refuse and point them at the correct one. + if (user.role !== role) { + const correctTab = ROLE_TAB_LABEL[user.role] || "the correct"; + return res.status(409).json({ + message: `Account exists already, login on "${correctTab}" tab or use a different email if you want to become a ${ROLE_TAB_LABEL[role].toLowerCase()}.`, + }); + } + // Keep Google-sourced fields fresh and link googleId if missing. + user.googleId = profile.googleId; + user.name = user.name || profile.name; // first priority is for the existing name (Maybe user sets their name themeselves from the user profile edit page) + user.picture = user.picture || profile.picture; // first priority is for the existing picture + await user.save(); + } else { + user = await Users.create({ + googleId: profile.googleId, + email: profile.email, + name: profile.name, + picture: profile.picture, + role, + }); + } + + // 3. Issue our own app JWT for subsequent authenticated requests. + const token = signAuthToken(user); + + return res.status(200).json({ data: { token } }); + } catch (err) { + return res + .status(500) + .json({ error: err.message, message: "Login failed" }); + } +} + +module.exports = googleLogin; diff --git a/backend/controllers/auth/shared.js b/backend/controllers/auth/shared.js new file mode 100644 index 000000000..447edc18d --- /dev/null +++ b/backend/controllers/auth/shared.js @@ -0,0 +1,23 @@ +const { OAuth2Client } = require("google-auth-library"); +const { googleClientId } = require("../../config/config"); + +// One shared verifier client for the whole app. +const googleClient = new OAuth2Client({ clientId: googleClientId }); + +// Verifies a Google ID token and returns the trusted profile claims. +// Throws if the token is invalid or not issued for our client id. +async function verifyGoogleIdToken(idToken) { + const loginTicket = await googleClient.verifyIdToken({ + idToken, + audience: googleClientId, + }); + const payload = loginTicket.getPayload(); + return { + googleId: payload.sub, + email: payload.email, + name: payload.name || "", + picture: payload.picture || "", + }; +} + +module.exports = { verifyGoogleIdToken }; diff --git a/backend/index.js b/backend/index.js index 2e1ab1049..55b9cd1f6 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,15 +1,21 @@ require("dotenv").config(); const express = require("express"); +const cors = require("cors"); const { connectDB } = require("./config/database"); +const { corsOrigin } = require("./config/config"); const venueRoutes = require("./routes/venue"); const categoryRoutes = require("./routes/category"); +const authRoutes = require("./routes/auth"); const PORT = process.env.PORT || 8000; const app = express(); +// Allow the frontend origin(s) to call the API from the browser. +app.use(cors({ origin: corsOrigin.split(",").map((o) => o.trim()) })); app.use(express.json()); +app.use("/auth", authRoutes); app.use("/venues", venueRoutes); app.use("/venueCategories", categoryRoutes); diff --git a/backend/middleware/authenticate.js b/backend/middleware/authenticate.js new file mode 100644 index 000000000..9577ddf34 --- /dev/null +++ b/backend/middleware/authenticate.js @@ -0,0 +1,37 @@ +const Users = require("../models/user"); +const { verifyAuthToken } = require("../utils/jwt"); + +// Protects routes that require a logged-in user. +// Expects an "Authorization: Bearer " header carrying our app JWT. +// On success attaches the user document to req.user; otherwise 401. +async function authenticate(req, res, next) { + try { + const authHeader = req.headers.authorization || ""; + const [scheme, token] = authHeader.split(" "); + + if (scheme !== "Bearer" || !token) { + return res.status(401).json({ message: "Authentication required" }); + } + + let payload; + try { + payload = verifyAuthToken(token); + } catch (err) { + return res.status(401).json({ message: "Invalid or expired token" }); + } + + const user = await Users.findById(payload.userId); + if (!user || user.deletedAt) { + return res.status(401).json({ message: "User not found" }); + } + + req.user = user; + return next(); + } catch (err) { + return res + .status(500) + .json({ error: err.message, message: "Authentication failed" }); + } +} + +module.exports = authenticate; diff --git a/backend/models/user.js b/backend/models/user.js new file mode 100644 index 000000000..c61497f08 --- /dev/null +++ b/backend/models/user.js @@ -0,0 +1,33 @@ +const mongoose = require("mongoose"); +const { USER_ROLE_VALUES } = require("../constants/user"); + +const userSchema = new mongoose.Schema( + { + // Google's stable account id (the "sub" claim). Stored as supplementary + // data; email is the primary identity key + googleId: { type: String }, + + email: { + type: String, + required: true, + unique: true, + trim: true, + lowercase: true, + }, + name: { type: String, trim: true, default: "" }, + // Profile picture URL provided by Google. + picture: { type: String, trim: true, default: "" }, + + role: { + type: String, + enum: USER_ROLE_VALUES, + index: true, + }, + + // Soft-delete marker. null = active + deletedAt: { type: Date, default: null }, + }, + { timestamps: true } +); + +module.exports = mongoose.model("User", userSchema, "users"); diff --git a/backend/package-lock.json b/backend/package-lock.json index dcbcb1b57..9f0021074 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -9,8 +9,11 @@ "version": "1.0.0", "license": "ISC", "dependencies": { + "cors": "^2.8.6", "dotenv": "^17.4.2", "express": "^5.2.1", + "google-auth-library": "^10.6.2", + "jsonwebtoken": "^9.0.3", "mongoose": "^9.6.3" } }, @@ -51,6 +54,44 @@ "node": ">= 0.6" } }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bignumber.js": { + "version": "9.3.1", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", + "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", @@ -84,6 +125,12 @@ "node": ">=20.19.0" } }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -162,6 +209,32 @@ "node": ">=6.6.0" } }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -214,6 +287,15 @@ "node": ">= 0.4" } }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -317,6 +399,35 @@ "url": "https://opencollective.com/express" } }, + "node_modules/extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "license": "MIT" + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", @@ -338,6 +449,18 @@ "url": "https://opencollective.com/express" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", @@ -365,6 +488,36 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/gaxios": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.4.tgz", + "integrity": "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==", + "license": "Apache-2.0", + "dependencies": { + "extend": "^3.0.2", + "https-proxy-agent": "^7.0.1", + "node-fetch": "^3.3.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/gcp-metadata": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-7.0.1.tgz", + "integrity": "sha512-UcO3kefx6dCcZkgcTGgVOTFb7b1LlQ02hY1omMjjrrBzkajRMCFgYOjs7J71WqnuG1k2b+9ppGL7FsOfhZMQKQ==", + "license": "Apache-2.0", + "optional": true, + "peer": true, + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", @@ -402,6 +555,46 @@ "node": ">= 0.4" } }, + "node_modules/google-auth-library": { + "version": "10.6.2", + "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.2.tgz", + "integrity": "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==", + "license": "Apache-2.0", + "dependencies": { + "base64-js": "^1.3.0", + "ecdsa-sig-formatter": "^1.0.11", + "gaxios": "^7.1.4", + "gcp-metadata": "8.1.2", + "google-logging-utils": "1.1.3", + "jws": "^4.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-auth-library/node_modules/gcp-metadata": { + "version": "8.1.2", + "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", + "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", + "license": "Apache-2.0", + "dependencies": { + "gaxios": "^7.0.0", + "google-logging-utils": "^1.0.0", + "json-bigint": "^1.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/google-logging-utils": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", + "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", @@ -458,6 +651,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", @@ -495,6 +701,58 @@ "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT" }, + "node_modules/json-bigint": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", + "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", + "license": "MIT", + "dependencies": { + "bignumber.js": "^9.0.0" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, "node_modules/kareem": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.3.0.tgz", @@ -504,6 +762,48 @@ "node": ">=18.0.0" } }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "license": "MIT" + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -678,6 +978,53 @@ "node": ">= 0.6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, + "node_modules/node-fetch": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", + "dependencies": { + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", @@ -807,12 +1154,44 @@ "node": ">= 18" } }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT" }, + "node_modules/semver": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", + "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", @@ -1030,6 +1409,15 @@ "node": ">= 0.8" } }, + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, "node_modules/webidl-conversions": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", diff --git a/backend/package.json b/backend/package.json index efa0f75ea..5e60cf861 100644 --- a/backend/package.json +++ b/backend/package.json @@ -10,8 +10,11 @@ "author": "Team of people", "license": "ISC", "dependencies": { + "cors": "^2.8.6", "dotenv": "^17.4.2", "express": "^5.2.1", + "google-auth-library": "^10.6.2", + "jsonwebtoken": "^9.0.3", "mongoose": "^9.6.3" } } diff --git a/backend/routes/auth.js b/backend/routes/auth.js new file mode 100644 index 000000000..f98e9c9cc --- /dev/null +++ b/backend/routes/auth.js @@ -0,0 +1,11 @@ +const express = require("express"); +const googleLogin = require("../controllers/auth/googleLogin"); +const getMe = require("../controllers/auth/getMe"); +const authenticate = require("../middleware/authenticate"); + +const router = express.Router(); + +router.post("/googleLogin", googleLogin); +router.get("/me", authenticate, getMe); + +module.exports = router; diff --git a/backend/utils/jwt.js b/backend/utils/jwt.js new file mode 100644 index 000000000..0ef7dcf2b --- /dev/null +++ b/backend/utils/jwt.js @@ -0,0 +1,17 @@ +const jwt = require("jsonwebtoken"); +const { jwtSecret, jwtExpiresIn } = require("../config/config"); + +// Signs our own app JWT. Payload is intentionally small: the user id and role +// are all the API needs to authorize requests. +function signAuthToken(user) { + const payload = { userId: user._id.toString() }; + return jwt.sign(payload, jwtSecret, { expiresIn: jwtExpiresIn }); +} + +// Verifies an app JWT and returns its decoded payload. +// Throws if the token is missing, expired, or tampered with. +function verifyAuthToken(token) { + return jwt.verify(token, jwtSecret); +} + +module.exports = { signAuthToken, verifyAuthToken }; From c2897cd91d4e269bc773fa0df38c2cb09b871420 Mon Sep 17 00:00:00 2001 From: Akhin Date: Thu, 4 Jun 2026 23:50:40 +0530 Subject: [PATCH 21/93] refactor(auth): extract toPublicUser helper and return user on login --- backend/controllers/auth/getMe.js | 13 +++---------- backend/controllers/auth/googleLogin.js | 6 ++++-- backend/controllers/auth/shared.js | 13 ++++++++++++- 3 files changed, 19 insertions(+), 13 deletions(-) diff --git a/backend/controllers/auth/getMe.js b/backend/controllers/auth/getMe.js index 37b73fe1b..abeb7dc62 100644 --- a/backend/controllers/auth/getMe.js +++ b/backend/controllers/auth/getMe.js @@ -1,17 +1,10 @@ +const { toPublicUser } = require("./shared"); + // GET /auth/me — returns the currently authenticated user. // authenticate has already verified the token and attached req.user. async function getMe(req, res) { try { - const { _id, email, name, picture, role } = req.user; - const user = { - id: _id.toString(), - email, - name, - picture, - role, - }; - - return res.status(200).json({ data: { user } }); + return res.status(200).json({ data: { user: toPublicUser(req.user) } }); } catch (err) { return res .status(500) diff --git a/backend/controllers/auth/googleLogin.js b/backend/controllers/auth/googleLogin.js index 37f966377..aea5b5179 100644 --- a/backend/controllers/auth/googleLogin.js +++ b/backend/controllers/auth/googleLogin.js @@ -1,7 +1,7 @@ const Users = require("../../models/user"); const { USER_ROLES, SELF_SIGNUP_ROLES } = require("../../constants/user"); const { signAuthToken } = require("../../utils/jwt"); -const { verifyGoogleIdToken } = require("./shared"); +const { verifyGoogleIdToken, toPublicUser } = require("./shared"); // Human-readable tab name for each self-signup role, used in error messages. const ROLE_TAB_LABEL = { @@ -64,7 +64,9 @@ async function googleLogin(req, res) { // 3. Issue our own app JWT for subsequent authenticated requests. const token = signAuthToken(user); - return res.status(200).json({ data: { token } }); + return res + .status(200) + .json({ data: { token, user: toPublicUser(user) } }); } catch (err) { return res .status(500) diff --git a/backend/controllers/auth/shared.js b/backend/controllers/auth/shared.js index 447edc18d..31078c0e4 100644 --- a/backend/controllers/auth/shared.js +++ b/backend/controllers/auth/shared.js @@ -20,4 +20,15 @@ async function verifyGoogleIdToken(idToken) { }; } -module.exports = { verifyGoogleIdToken }; +// Shape a User document for safe sending to the client (omit internal fields). +function toPublicUser(user) { + return { + id: user._id.toString(), + email: user.email, + name: user.name, + picture: user.picture, + role: user.role, + }; +} + +module.exports = { verifyGoogleIdToken, toPublicUser }; From 5e9c2fc51c7f0f7ad1490878de40a4034377aa74 Mon Sep 17 00:00:00 2001 From: Akhin Date: Fri, 5 Jun 2026 01:07:23 +0530 Subject: [PATCH 22/93] feat: add Google OAuth sign-in with JWT auth Wire up frontend authentication against the backend's Google login + JWT --- frontend/package-lock.json | 42 +++++++--- frontend/package.json | 1 + frontend/src/App.jsx | 97 +++++++++++----------- frontend/src/api/client.js | 31 ++++++- frontend/src/components/Navbar.jsx | 48 ++++++++--- frontend/src/components/SigninModal.jsx | 99 +++++++++++++++++++++++ frontend/src/context/AuthProvider.jsx | 62 ++++++++++++++ frontend/src/context/authContext.js | 12 +++ frontend/src/main.jsx | 18 +++-- frontend/src/pages/UserPages/Signin.jsx | 6 -- frontend/src/pages/UserPages/UserPages.js | 2 - 11 files changed, 332 insertions(+), 86 deletions(-) create mode 100644 frontend/src/components/SigninModal.jsx create mode 100644 frontend/src/context/AuthProvider.jsx create mode 100644 frontend/src/context/authContext.js delete mode 100644 frontend/src/pages/UserPages/Signin.jsx diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d64167c0b..6585d7739 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,6 +8,7 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@react-oauth/google": "^0.13.5", "@tailwindcss/vite": "^4.3.0", "lucide-react": "^1.17.0", "react": "^19.2.6", @@ -58,7 +59,6 @@ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.29.7", "@babel/generator": "^7.29.7", @@ -268,6 +268,27 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", @@ -544,6 +565,16 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@react-oauth/google": { + "version": "0.13.5", + "resolved": "https://registry.npmjs.org/@react-oauth/google/-/google-0.13.5.tgz", + "integrity": "sha512-xQWri2s/3nNekZJ4uuov2aAfQYu83bN3864KcFqw2pK1nNbFurQIjPFDXhWaKH3IjYJ2r/9yyIIpsn5lMqrheQ==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", @@ -1086,7 +1117,6 @@ "integrity": "sha512-eRwcGNHve+E8qtEQSSRl6urh+rFop4v8gm6O8rGv25CodbvFdLjA1vVQ1KkiFE0w0UPOnb8tDiFKL5lp0rtY5Q==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -1133,7 +1163,6 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", - "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1224,7 +1253,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "baseline-browser-mapping": "^2.10.12", "caniuse-lite": "^1.0.30001782", @@ -1385,7 +1413,6 @@ "integrity": "sha512-AyIKhnOBuOAdueD7RB3xB+YeAWScb9jHsJBgH2Hcde8InP5JYhqrRR6iTMHyTEwgENK54Cp44e4v8BwNhsuHuw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -2293,7 +2320,6 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2354,7 +2380,6 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -2364,7 +2389,6 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", "license": "MIT", - "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -2598,7 +2622,6 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.14.tgz", "integrity": "sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==", "license": "MIT", - "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", @@ -2723,7 +2746,6 @@ "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", "dev": true, "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/frontend/package.json b/frontend/package.json index 595b710f9..2ca2263ae 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,6 +10,7 @@ "preview": "vite preview" }, "dependencies": { + "@react-oauth/google": "^0.13.5", "@tailwindcss/vite": "^4.3.0", "lucide-react": "^1.17.0", "react": "^19.2.6", diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index cf3936553..083ef3da2 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -3,61 +3,58 @@ import { Routes, Route } from 'react-router-dom'; import Navbar from './components/Navbar'; import AdminNavbar from './components/AdminNavbar.jsx' import VenueOwnerNavbar from './components/VenueOwnerNavbar.jsx' -import { Home, Venue, Category, Signin } from './pages/UserPages/UserPages.js'; +import { SigninModal } from './components/SigninModal.jsx'; +import { Home, Venue, Category } from './pages/UserPages/UserPages.js'; import { AdminLogin,AdminHome } from './pages/AdminPage/AdminPages.js'; import {VenueOwnerHome,VenueOwnerLogin} from './pages/OwnerPage/VenueOwnerPage.js' function App() { return ( - - - - {/* Public User Routes */} - - - -
- } /> - - - -
- } /> - - - - - } /> - - - - - } /> - - - {/* Venue Owner Routings */} - } /> - - - - - } /> - - {/* Admin Routings */} - } /> - - - - - } /> - - + <> + {/* Global sign-in popup — opened from anywhere via useAuth().openSignin() */} + + + + {/* Public User Routes */} + + + + + } /> + + + + + } /> + + + + + } /> + + + {/* Venue Owner Routings */} + } /> + + + + + } /> + + {/* Admin Routings */} + } /> + + + + + } /> + + ) } diff --git a/frontend/src/api/client.js b/frontend/src/api/client.js index 2104bb4c2..649c46ff4 100644 --- a/frontend/src/api/client.js +++ b/frontend/src/api/client.js @@ -1,19 +1,44 @@ // Central API client — a thin wrapper around fetch. // All backend calls should go through here so we configure the base URL, -// headers, and error handling in one place. +// headers, auth token, and error handling in one place. // // Vite reads env vars prefixed with VITE_ (see .env). Falls back to the // local backend if VITE_API_URL is not set. const BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8000/api' +// Where the app JWT lives in the browser. +const TOKEN_KEY = 'bmv_token' + +export function getToken() { + return localStorage.getItem(TOKEN_KEY); +} + +export function setToken(token) { + if (token) localStorage.setItem(TOKEN_KEY, token); + else localStorage.removeItem(TOKEN_KEY); +} + async function request(path, options = {}) { + const token = getToken(); const res = await fetch(`${BASE_URL}${path}`, { - headers: { 'Content-Type': 'application/json' }, ...options, + headers: { + 'Content-Type': 'application/json', + ...(token ? { Authorization: `Bearer ${token}` } : {}), + ...(options.headers || {}), + }, }) if (!res.ok) { - throw new Error(`API error ${res.status}: ${res.statusText}`) + // Try to surface the backend's { message } for nicer errors. + let detail = res.statusText; + try { + const body = await res.json(); + detail = body.message || detail; + } catch { + // response had no JSON body; keep statusText + } + throw new Error(`API error ${res.status}: ${detail}`); } return res.json() diff --git a/frontend/src/components/Navbar.jsx b/frontend/src/components/Navbar.jsx index 87904cec0..76002eee9 100644 --- a/frontend/src/components/Navbar.jsx +++ b/frontend/src/components/Navbar.jsx @@ -1,12 +1,14 @@ import { useState } from "react"; import { Link } from "react-router-dom"; import { LocationModal } from "./LocationModal.jsx"; +import { useAuth } from "../context/authContext.js"; export default function Navbar() { const [menuOpen, setMenuOpen] = useState(false); const [searchOpen, setSearchOpen] = useState(false); const [locationOpen, setLocationOpen] = useState(false); const [district, setDistrict] = useState("Ernakulam"); + const { user, logout, openSignin } = useAuth(); return (