From 99824886f0e5ae07326a69faf3380a07e05de810 Mon Sep 17 00:00:00 2001 From: nafanpe Date: Mon, 25 May 2026 16:54:38 +0530 Subject: [PATCH 01/95] feat: add navbar, home page hero section, and routing --- .gitignore | 34 + FRONTEND/README.md | 16 + FRONTEND/eslint.config.js | 21 + FRONTEND/index.html | 13 + FRONTEND/package-lock.json | 2746 ++++++++++++++++++++++++++++ FRONTEND/package.json | 30 + FRONTEND/src/App.jsx | 20 + FRONTEND/src/assets/Logo.png | Bin 0 -> 49315 bytes FRONTEND/src/components/Navbar.jsx | 29 + FRONTEND/src/index.css | 6 + FRONTEND/src/main.jsx | 14 + FRONTEND/src/pages/HomePage.jsx | 14 + FRONTEND/vite.config.js | 8 + 13 files changed, 2951 insertions(+) create mode 100644 .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/src/App.jsx create mode 100644 FRONTEND/src/assets/Logo.png 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/HomePage.jsx create mode 100644 FRONTEND/vite.config.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..35fa4f81d --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# 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 files +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +#linting and caching +.eslintcache \ 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..f94d687d3 --- /dev/null +++ b/FRONTEND/index.html @@ -0,0 +1,13 @@ + + + + + + + frontend + + +
+ + + diff --git a/FRONTEND/package-lock.json b/FRONTEND/package-lock.json new file mode 100644 index 000000000..93e5ec3d6 --- /dev/null +++ b/FRONTEND/package-lock.json @@ -0,0 +1,2746 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.3.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "react-router-dom": "^7.15.1", + "tailwindcss": "^4.3.0" + }, + "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.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@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.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@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.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "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.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "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==", + "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", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "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.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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==", + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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==", + "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==", + "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/cookie": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "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==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.361", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", + "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.22.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.22.0.tgz", + "integrity": "sha512-xYcDWrpELkFzz9SpZ3PlI6Eu6eD93Yf0WLDRxikGhWJ3MAir2SNZTIVCVZqZ/NUyx8AdMc2gT9C0gPiw18kG+A==", + "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", + "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.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "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.1", + "@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==", + "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==", + "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/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", + "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/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", + "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==", + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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" + ], + "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/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", + "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==", + "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==", + "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==", + "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==", + "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/react-router": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-7.15.1.tgz", + "integrity": "sha512-R8rl9HhgikFYoPJymnUtPXWbnDb3oget6lQnfIoupbt61aT9aOhRkDsY2XRhZRyX1Z/8a5sL74fXmFNm3NRK5A==", + "license": "MIT", + "dependencies": { + "cookie": "^1.0.1", + "set-cookie-parser": "^2.6.0" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + } + } + }, + "node_modules/react-router-dom": { + "version": "7.15.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-7.15.1.tgz", + "integrity": "sha512-AzF62gjY6U9rkMq4RfP/r2EVtQ7DMfNMjyOp/flLTCrtRylLiK4wT4pSq6O8rOXZ2eXdZYJPEYe+ifomiv+Igg==", + "license": "MIT", + "dependencies": { + "react-router": "7.15.1" + }, + "engines": { + "node": ">=20.0.0" + }, + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" + } + }, + "node_modules/rolldown": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", + "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "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/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "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==", + "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==", + "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==", + "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==", + "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..c9e0db155 --- /dev/null +++ b/FRONTEND/package.json @@ -0,0 +1,30 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@tailwindcss/vite": "^4.3.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "react-router-dom": "^7.15.1", + "tailwindcss": "^4.3.0" + }, + "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/src/App.jsx b/FRONTEND/src/App.jsx new file mode 100644 index 000000000..bbfca4360 --- /dev/null +++ b/FRONTEND/src/App.jsx @@ -0,0 +1,20 @@ +import { Routes, Route } from 'react-router-dom'; +import NavBar from "./components/Navbar" +import HomePage from "./pages/HomePage" + +function App() { + + return ( +
+ + +
+ + } /> + +
+
+ ) +} + +export default App diff --git a/FRONTEND/src/assets/Logo.png b/FRONTEND/src/assets/Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..dcdcc70622d168a285af5b84a654674d3e1a3c11 GIT binary patch literal 49315 zcmX_nWmH^Eur00ulHl%6aCd?Q2o~HexVyUs{cv}82<{pj2A9EgaGAjedEC3+dw=@$ zkE(Neb#?dYy=&JW6(v~=G$J$@7#Iw>A5yON=npRgwL(cd}UJHeB){6ldZ9glr%IPN6G;-B`O={ z9VVX;!Y#~Vi8D-fzkc_jEbNr;e73K@d=rCQDhbMY&dUjoC;2NoZmqYOdw6(QczE1j zmT&}4yBg=gLr5}ckr!FcrnRy`{ldsc0_=cb_-uFx=1#DPSOTN_OvlsR8%J(1e7&pZ zP9nGR9GUBm^SslRyoeH=O8^1%e@)+x9wip@f1NfOT$S^jFIbRC>5F8@ZB9U+rUwRu z6B4+9v+St0Y>EC5FRE|vJ%jhf&28zMVf zBB$@S+sWufF4C>m_rtUxLtcK)JI?zsGt4*p3hW?RE}< zhT2inJvuU-koN|EP(tcgWlonjk^gJ=zj646j2X>i`v&@KA(V@3-jmB81mOx)nK|B% zJND-A>z+P-@gj4@JQsn5%vJY*b|r#MK5Q|gt$}J(o_jDefR{W@_ufqs7h}Uup#L%< z6(6zWBjIN#(sJcm^k(^($PE``dd!`Dk16HH;At*#ekbEdJPI$3Ja}mI z|HS@hPVSsoMx!y^PvxAK&}bfH4=ZQULGZ9@_EX}FtFf4iac{LZrrmD;$25iGuG3oF zVp*`PVh&(myCN-*^fGyMN~ZXt8pBj2Q4(Vi%~DP|i{-2V!~3_4OS5ZEZ~ymT}?U)_!ufGSD-kAGJ$ zh~p|6z6)nL^UF_qDXx_~w#=U;GgT~GS^-;JoVNIqdK|d3lbB-2DO=x z)6VJP8KI!{hJ6_5>eXN;^MyeFBbV|F>}YULazT%6Phh4q`&?(B0N3{V+fjebZ|6Yl zyVcva?UkufiwxJLGuU&O8T31SMezB}lr7-go5#pi5c512;lkkTh2LLRU*Mp_$)Hf{;RMS~p^T&5{&M#K z1EFh>)g|Kw`Ps@(b@C6lp9q)hFHfW^y@ywC1EPKXm*k>=2hRUlTO>b7`1zOX;X@|i zL!wU1EiaerH^~pMH(+zNofX-VARQm48%TUS_`?45e%rhh{*^;{dp{W-8t|Hh?wnr& zZj5y^Ns1yw*ySj-N6PXT+dBMxZu*}P1w!5q<_Yh{BS zp?iH8sz{0G9z~yg_Mr0^e_#TyH5iImtp!a!8Gm>qJ9XoZU7&(AOkrms{;s>{vxmG+ zyY3ht^s|uk`|o&=#pVDbx%*uHMH>Z*L$ecvK^u+OGV2!u(vhu^X=Gb#&BqNhJ4w`n z+gserv~Cf2gPvw?!vn}e9` zr>jw;_WRyqtz2l^Xnx?FY=SWK{BX41fcSFAWV(1Oki=;M(j#9SSisv*ur;lrh!|G| zDUz>y@mXwZI9WMesxZtC@4eD1O7Mo90M72et@;6!$w&mj1BHdl{kzn)y+b2pXwcEl zNa5Ng-Is&qHnP=4!K0cbqx9+N-ck#X)qitPVBq_C{PYr{BsF~GCk$SEEO&fTj&l(+ zs|v4);?QYOpWE!~He2Kny@)I1kE0L-v1!OBXc4$Pt$9BlxtS-nUr9`E(0!2jj><&b zWGUj9>^lQNe$}?W54dQHT&x!j9D6;j$G`=6Z&6$<*Pqv`l0cqU#I9ynMRdKNU3sC( zftLjj%zYdwVoz(5M_PHx1V{QWr%vS^P8HZo$br|}?~w3U=@p4tfbgG>#hVD9@D##M zZLVApLc$KOkhVOi+a$XDhj|%7T|T4|o~p9799GB`@cYR^NBi2qOX6vHN#-|Gn2piz zjOEncA7Jjj!(66k#F#WzEBbqZ@IQv;uM^oh9P%Sy5gos;8pXDXuUs>Oia7k_@od$=LV{%?#PRT$750? zQ}m@OS`?N8)Q^?KDdw#cB~!5y>+0(g7mK-#mvGm}sFTp!~QRWF)}@T=7V2N49F zi3n-6HEgWR5j*k5Fsw0{j|F1}2`D~odCx~{Op+;NDGNfo@%ivs+i5heg~Fu)j9zzi zob$S~9hxOtTeUR7m`r83qnC{(o{mt%bJ=`n=R~4Qc@aw$EaJ~XVL#`92q^BjtfjG( zL6$Fk=k|e3cJ-el96z05XitRSNk(ma@-z!Tb~22ba|mGtjXs+2S@DT-DRe`Y#$Zbq zCB8M048HV4lFa(9I)J8+{#k=w*6Q3&`eBk-)SR<|x;XfWI3wX&D&XujPz#8);qYf% z=5vsrmVpHbhZ3%hkrtwH2fxWuc54;73Gspa)4PaYa-~`S>QDIyY>#jWR*BNzhjc0H zyAO<}=LZJlj)AU1qu`eX-P%!XE>a<+3zU`YR91xdYDzf2hP(=a&Oa7@Uuav;xExb3 zUPKBqyF3PnJM%Pr`bh+k%mbl>z{o(4Z%w^SYKxdsm3!V|x$tHPVY`BlAJvrYk7n~N z4j6w{CQA?uLPSoLB4W3rxgSjD3Bwptkc1O%re`l?@O?GhSuNu|%B9*&Haq94Zh8q3 z3n!amvyl-K%+Rg4I-RkUm>&b8SZ0jXue-6g?pw>xmnbae9#hNno`rE`0OzVBUCx7e zM1N9YT>hakYAXqKI4SA3lge~_g{fVN&Duu}{UD?1`JVN*h zZFWUgna(frChFzVJ>rwht zIl6-k)gZ&vzhG)9*OS&wMNXe1Es?xfD7?qjp|MuZNkq6FbK18aWoK5npA<>wDo-JZ zb^hz?{GcXRpY26bTwGko!ytsJe!u~i(+k5Hab*wqJYu-5H==jZRgQLA59pTvr&=L` z`+e>yCai)*W{gin1EH4^R@#te6Trz4_yG*^;G1?}G<@+|j@l<$BVshmhQ*#(#ag{_ zMgkYC3^$hk9@WL8U;Vv9^em_A7P>3$S}1Vwv}5wLG`!0jUc8cXcT+Aqn093A;>i|;$q#&hb1O{u_96wJ;rJHBwoHLN$@!xbJsMS<@c(12> z6uPQF1DUoQfGy`{fc@OjNW1=x`_M#K3%>h?z$jc32wU1!_@nkenp?(sYxxWswc>(> ze`DM#m87qCk-TrB_xtWI0{DI3G%Px3G5#U!%ctV(DKuo1Xo2Z}s9|5hAGBEhzKz4! zF0-FwI=+r(LB*?PVWC$zm{g3NAEbO3lFCen zYmn(Mzp_~rSKfVR)VkS)v;zKSV{q{b{E*5SkX2?_SnK%x&^GRCipv{~6}5NP6A+$2%@x=*`$ofGhDwH! z>8wjAUE&rkY!=Jb&+S|pjVCFI0DadyPnx8Lrklluje6HLp!kP2uZNcv<6BeMCs3-i zhW!^~S>O`(f_MGryEsxNEyu8xhpxSsaO>9`0a6i6;-oTF<^*+q2Lx4IGc&Vm=B46P z{H^APK9W8Dw9zX?UUG&|p`ag`nY1Y!Xkig)^d`{|b>1SSw5xI1uFWGE8SCN~@s)D5 z|HlP9C&He*%g8g&A8sINasP=oy!hpz82$k*CN`=Vn$9o2hd3M=tbJa?x{X3Duge7#6H z%3DrTWnUs94KEv%T|zrXDBAfFTc!bS$L8@%Z$zw>52YOeeqZn+Ry!e6>Z3VLon1Fe11-vu$|&=-lr0g{IoC__2iNgL-4L^p=DNM#nOrJlwZ&)|xZ=8fqlqILwrH+{j zAGbhb!+i=pCunypxVm$@hZD?FdEw*a)fEik@x)ra`RBY+Cj3^T{++;~L3X{kVwpH3JfOvir_FLggTmr<44&rT@oxrcTQ zWp7`t-NNhC-@&pep!8rJ*F3;#+-AhH-8DV_N~TRe?S6p$y@{pX3{}}6dkt}nA%|Ge zGZTENk)@oj3@>vAc;~PriU&KS&wUpYKMESqOjlh#4gnKbILfvy+@IT(A0lfN`q+*6 z$98V^49FwQCjAOFXmulU&Y?1I?p(I|MH^Bs0zBR9_ZaR6iMB(5YxzvuzLZ_R`w)F+ zofakOCiGYaX;KIm$)#_W5WEvWq%VNClDnnu71G0Ye$H8?f?G!n(C_&zG;*dECIiE^ z`T$~W_pY9=ZTx$G?^tM~s7bdnK0Uo}GI~Q)T#Vlu(J>hq*-G`k zD*r4XzFo!G5a6Kfy4()!1eDdjTVHQ*UBnd*&h~L2g@%T@nX15fq4L5$>7;_EyDQs1 zc?eP~3QpK0J3gt%OWY*gzQKKyYHD)M#Ib=@DAyPPo;7uxM#N5~2%Y7qF40W(V_V*S(~lb7(+YSzE>1Yf-=k2# ztn&4HyN~@nQ2=a8>vCxECC>7fK|(b@&IH`7Pdtm{<$&GX1X$#DI9QtR*@b~2MwJ8T zRpmKO%N-!4NDsk-sD%2Lw)psbs&kUp*8AZ>u8qRW?r{m#PLrVsx10X{ zbPAH4lc3MFjH_@H)-##>BjR1_((1LM?->hPB8sO|wB7gTP5E=@SMM6mZwIol!z5a# zJZ=P^3i8+qjAt)ARO&T9C&c-m^g=HuL4V)9^%ER1RdUF;;dA~S-?E&ZH!-On>r4;i z@)@+_d>-*v!p0y}mtd#^^Y>~a+Z490#|O0cag|CO89g*yvKY^80> zll1(J5FTzAgL1$i{(>i30Z%*2WqnC@sY$hoVmZO}l}m>L70=$T_@IL;JbvyH6*1f( zB|%fSIq(nd_@GbyM@;4we9(S$5q z?~GqL4JM>W8|7NM*9sVol1qbHo3NdI_NC2dJR7)an4*K+s0y|;5{!jA@)qsYITS*J zb6YoK!2$(BJ?rR=$+q`%;N^ue&&w62tMc~~jEK?fjGkKE&pJH=!D$fnw%3SWb^0U z8ea`gnN+GZb&CEwh?N)WMW?9Y%g#g-^#z|U{Tc)J^mSb?S5gS-KZ#PueT7|IM0bAs z!8(^9mbrR~*8}U0(kw>f*fk#tg|y|lJocd7s~&ogM8;|q6ol5qMqn}>s$RtNJg~?H zoYT}SZxd*omFD7?1LpB;YAL?}9q46_pBTJMcM0!|&jQl}Tme3UPP%h5)h891ysn+m z5bqBJzSDnh81Lcb!2>E-=GOczRMY2sRE1BYjQVEc)d-C=w={ShyojCz2sw1$@VZ3= z!pniO$%bEM8;h|~h^X?1<~pQ@si~De!Q)u5X;+Kaxs-UL4RuzHeHL8aC&Q~_!1&~4 zty%UpEyCzi*9#%8#Z|iAb6WgJD7QXdM?ui-q>F%|1?*FGc}CYH&Dwfy*jv>6eEUSk zAt?fE7Zo4_d7m<}uGit;KmQuuR}sg;GSFne)Zxas!94?~J{=tyxHh$jb;?h;!}el0 z$6%t7PfuJG`4H&1Al!VkOQDR=!&M&i*e)$K4Dr zfQV29Z#t-%oa4b1{J`_Cvhk&iE<~zd2U*&e(huPT-zUWsv!@_r2ZvNLit+pTe4Em_vSVfO(av#O;4)oeP~-BVmfflnvZTcfSmC7e9Y z+di*%SUoSZ;q{uE%R(R6TxJ7xWRnaZr_#8Cc}m>8s8K%ALkLAUkLXaReD92`Z+)Un zWcFrqL7hnWXs3p6lP=p$vEuDhX-$LHgFRpUJEJi&>=$^!j1Wa!aLx|x*bs@lZD2DW zFU062pQ6c*|DC^pzkseHGoxr6@Mq)oeJ`XAhm_$&XSmbQx?s~`Vf62{FxKB$Tli;f z#QAn~jXH3UhkVv7EDV~oWz39;$y8;%Q3p8XuQ3)meXj3ieFeoMLpO}v?&mPvD9EV) zZ@J_G+5rm6jJL0wb8Z;03Y}&3O?|GQI~ zT!QD+Fl+#`j#@gK5gE!WC+2*Yqt!N_w%lb(Hsn)E0%InWzEb`W`N{ha{foYgK*kQ} zjndfYj&ne%(GXe{qq8e=v#6om8u>E8m*pQLCT_tew5fW@YWCRDV5leTbi%Ba>-+8| zq_CE|<-VCpuA^RoU+DoIg>OLvQ$@Up1g(}bG>5-uV&$HGgALvnEPO7}^c$@wm&vHgY~a!SA~qY%}tB>A>H* z2cmD^bgURXUiJw)PfJ%P=yaTp`QL;d`HB>LaJ=;bXM;oW0`EoXeW<rNHT|HY1+{ z0eru`b{@yR{V^^;*L?}k)yq&ZK=>+-zD^esa#*beAd%j*{mx!%*WNR#XUczcyI?z7 zr+XK@lG6m%pszQ8R22h+>RoUQi_V4-BlbnKqn7!qysPLE*^G~jRU9}ku)$s&5H${as zJ$Zhc#be0?w=^xpFY+y-EP;k4U*Tk^6-Jd286M2@O%acO|A(2b<_I!IY%4t3w#LE49L>lPv;tdMCUR?tDUk@*R^im+KFFVfVc~9@T ze_akc)>}8A^(*zd?h}ll+=qc=*7luu)EvJ7#6S=5(}&L;e$7)8UyTmL(y?=A@ZIxS z8b9#*U_rUZt8>et?;d!?YR63$yH5JpD?xur!OF={s&aVqB5oumctKXc-OT#Vz2lD1fPdQ1Y=M z!LN!Jjt^ZEfPgaF1g}2M7eIN<>uXnnuJa`ZeO>Rha=elEOm&)p%b!SrZiWOIr?mcp zvA^b}3nnyyUoB3R-k>x4S_TbA8BMJ1N5hb9cgw>LwwLrdX=NgvbDsArD6!r{zRNDT z9skie&AwkPqc+RhXb%C^{=R)rRX70@Jh>&8uYql^LOd=x_@!1M7)nc{5L{hi4@Ag-h!)7OodnOVF z5$0-)Mcs=4L$6%%uUJ3+>+uAGjV?92RNXx~qKa?v`K1FbQ!R%@<*D;n_I}5oEvD8K z;lGeTTT{!sEe^BlrIrvwEAd||Y>V`u>7q~6W*xl^*S7R`>ems8gT2{T)jwV5axIK^ z1IgXDgn0X2ZkJX_y6#1whY4^1c4POwiBx4Vk0oz%zv-hHr-@xv7U+i zf_Y&B4QPVebg+g&sn~JV9iU+AJvZ*p!fL4$hY`cJt+iEhtTg`ey&K#!Z zR&Ce%`8%XH_#XgpVysTA$X*jZw@g4vnqRS2%rCfzDxI&y=o&;W@&C90jXjI6@^R$` za$)AZFh*P(iyY~KcgWdYJrBpdy$|v2dkN}B@6dU6AEPgtTj>M7M%(j4Zv=GDXDj&u zTS^Ntw7rPSky$^`?dr<=Nq-g^p!@-{MVo5x2_9C6m*I( zIj#iCsriSJ0n0+aWQqdordC5oIR%|%^WAreje5X?zA?HGBSrh2WRvX>vAC9p*43)? zwE`dQ%W!mcbzg_k%;N5m{^+Ud019;IB_-34PdS4!w8L{ z)SF?|QE$79M7|h_{v8iouD72FJk&zEXv*(734ROD<|X$TE;HiBvkNGg+i31sd$?xa z2B*K-}X#5f^XRfzg}BagM&^o<*vck@YzL3D=;}@hYAS64(UY%C~BxEsNXg>bnKYZ*>vZfcuK{E zuiWK41GI8m_xhAXK`+%tfzNAzbz-7>&-Dv;o4(t?sgrUxfs-7bwnJ4nd;BUf4Wgng z@2p@G7>an4iuqo0Pkrc3%>4bes_L=G z`4grbky$}Ks`97EmvotRo?j*v*hDT}p*m}IdjXH?!ov(JNq>5OCt}0ki~5*OwIGNG zBbZ&*;nx@{DrH(Sw$xrEA$oqznGPNVhQp(iWyItqh`HWz#f#c6Mf%3XKgzK;9HUG0 zygv>;X`* zebzre6cBk2Kqp2QzR;R3D^#6sWbn$HDU3y>#@1jmbg<`}Zff(ppKv$IuPYYk%>B_; z<~I34r2Q+eEXP=x1U-gDK_%upcNGtTW@?sSYW5dvQB(n(ktC8vG(%x^+W;%hK;Ell ztz6IFr&H3BX5YRzB1phI)#DvraDX$)IZ}SnR?VNdCXuX2KBd*y8e2=FICT5jzIoYv z8SF~IBx?rB?!kbx8ZuS6&X9lXP-9WW-nNUyF|{nVr|#F@zsV_smv~VU#zGs;sUN6u z$QH`r6vlc`I(21>+{)qt4}Xf>6JDu#m)jnugTTV!FW=*tS^Q?SXsIy4bTwrR3^nfpj}qQ9QiUbQa2ig>~;w35Fo@=VC{C5Yo#Vu z@sW+%*~i)3c;LH~#s>fmD3vFcSA_Tl6}+GXsHbxt4$9M>l0V+f(Y*gKE`Ar--GC$v zIT)teEI~r$H=j(5N*fDw9#aFrW(DbH1)>cleamfD{e)ukWlGlDiU(>{#9;oK2fkt% zBcnxv<7#ev){6UYy6R^}EDa4?eZLdqPffI|FEqqtIhdZKxikYSTfI&KwfmlXC;E$q zxUi87YZDY)VL+@G5n=FeTp?wl0L|hTEYV{`devrPja^fc1tkq?}i4Mfk!eN#mYiBA7E%H zz6!eSwcA*fxI>mdJUzX}yI8zl<0-;VSO1HMv)<(xp}u88;H`B1So8;}{eu4;9q${k zw{?Q^5H550LV+yw%C1OR2PHKbE0Gsn&Ne`yx7h9TV+yz3!S2VsiBw6~G1E-Zpoq0A3@*(RyN zcu@%1p~K}#So}z77a>)p3Q|oZzOX793(saV^_O22_hC>;BjMkPW@Z332jhS z+Z4WtdCn?7vw`%QY34k5j^=kdzDJ^8|59S?weV*%dW)Agu`#+HoJibUbb%dD98TM> zD$unzwX0DiTq=+*sk0c{Zy)o`)$la%gxGU=?y0SdXww!L6T&v9@t^#BgvGF_hp**{ zF7k46^EzJNv-_=BF&ip+T`Qh4(VEGZzJ#~tF1?r#gr)Z08K z*Z(<_&_%rk3~6Y+@>#U=>iZrh_}RaOdcb74n#Bg+;J92)9M@9EU{4GZaF2pgfM;UJ z*c#P+e83YbCC;BN-_I$$7)vSq@Wmb8A?W$-_oGu=wg2-M_RXvO_7WdWl#VyyTZQ?3 zK>>Rv{Chv5;dy+l+8axnolHuSe|)57IXw2G)u;%Ka3Ryrpay{;s+vlw3Sl#DMhD@O z?iMkxiz*ET&zORniJ5uL6frmnkgi%g2IUzY4l_o+zuVq$0`4tsx_;3I=zU2jRMoR& zO7&59ERZ$4OClgUQQ)krSpLphI`Wi?&*+R0wGh_YDqcFb=w^kbnESp)tyS|j`&WUr z@FoebD*}_xO4AxU>`TJC8L(e8%mhPMT18MiOS=qy@9ctTZN|n6I2Ulb5$vHZKA_Ws z$l{Ay?OqQ)E_SfAvS9Lsg{P3sbsc!0Z0_w5ZP;mPY{69!qJaU(=9GRO279{jykY}s zfTcLP@Xi~wx|1$gn~RicNCuN__(&4({n;|n2lWPw>)IYn# zhboLNDsU~0M4ZM|`BTVX|ML#Lmg`*{H zUY&5jB@GH-o5NPEdVvR`KT3A%wL@+7oi>j`D~1mdoeTK*Jq~Rh1z2X%S0^8$p=)Ks z9`J^ki1P)x{_|eL-EBE=uO8Z_KlCqr7*C;K;EXgv@2hDZf z&s!XD&y+E>`&u^LUo~Q7MjNH%Nuyb&@m3Sx*YSqV`{H+1#oHWj{5wE>ZRn)99~vq0 zei__;u8Kt}BGynE2`+eD6w(VuvD0M`L+F*j*3Q_ z&BfAS@Wxb`PEa3I2kR{!EP=dj?TXnZH% zrVdL|@@A$zZ)JEFC1WXINomSBImJ3XYnLC5{!$iPA(LnQdox8TlkPS8FA2q7M0>VH zpUO)SEl!>`iH*Pw8qpH)%N=s%ph~8m#mwVvAo0BXWV0B|%ZK!B0;MX|LT`6ywBGQJ zl}gCiu}6$S=p?qu==|k4(y+zZ0SNNuI@50$a5s(Jsf@JE2-w& zq6Tp~o%CC|L<_iFdN+UzrKpFjHwia@t_o`vHYOY_)+ z+*XijMK{Cmc6U0(l=n5x(uL$FiVxf$CwYlaqCb)?&-r-mR#h4X9kylrE^f_i(J@~O zjcWT?7)jhdyGUhadwym~nIj+~3|&AYEVfWB)|`;grMy0`;S2Cw|ZWqy7?DJ%q>Rorvb|EissQSo@+ z2`O(L?SLJuK%2m)nor@<)!~l}5l*f!dLInv*BVO=^|R!atW^zPCK5Q)F4fV3O&KzD z$j^M*;!7~<@kscDv^M+%w^j-(ThPq^PhxD^v6|)N0AcKbbq8^S5c1<}Wbl#RB0L17 zRZv+?LKb!f=j~wPon}v1xjTR2L!JG0o~O6$?}lG7X*yXxd-Nmu^93|Q?QEh$W0`V) z@Nauy;`?F3%UWoF31D#p=07z%a})ZJ->DYF)}X@8KdR<9|3R8cY@K8HFKUD@UbC72 zJq)U+85PSc-bU3t{B#=kxwv@Iu{G+Fw*-GaF(d~hZZEX4zkD{XG0QJ_{dGWZyu0Xy zAMb4*y?-+~9G{?9-}gevYP=oG|A6Xk(JaGLHOOaa`1b#61juLvu_GvznFX(ls+>o} zTxCCXr7uiecMf`Elz!5wVZ^7HMPVq^Of)?jp!iVUF3fD3;%ns%=c8~(cSo5VTSvLp zRQMvg0|-Jt2sp@=Cb={c>ZLrf@Q_Dn5rbNu0eWB(DvF5OzHT|Z(C6+AShZ(5A;lM92@A@OQh zt;S^DAnkmd6!bo+Cll@$hJ#9kbtdw^t&G^hqw2RSAL6e!%GEgL@*wX#jiwdZoq1{7$hc%M02)&@M-jggX)Y6NJ*Hk)W689X6UmL1}=hFc__Iq=ajE$_N2 zJAzV{O8^X5GK^M1ug;g&La7AQXZ(2O3EIG4t8r%~3o|~wmQ~wYs)ws+xoaGGvp;UR zD-b?$4!bqW@#XSQ#)*JG^?K#k1zF@r`-i>~-D~6g^Qun?4m;tc-leOO2#BSqxErYE zrUFR^dTa=Qx?Q{XdIK*Vum%@4?YYjLO`{S-e5XsTOYaS|Y(<%|;0|)WJ2By=uJZ?} zjj4r|+_pk!!l_&tyAI({IW-Z*XrzTqu_vo?_O7I3|C3O?a+plQqej`p7jzF^GS6+-OzZV|+*4EtAm zVfh+;7P!yP!^wd}cl|&7rWW4$z@c86_oHIp^yd%b^fT5mnUN-l{9>OU!1eZj;eB zPE%Z*n`l>BmlSivaa0&q3q<^OgI!#aDxJ2i@YFV4T=7JUQSWH$4Yy!*W}*3Kq2T6L_Y+po@cw#_cPD=> zn%TDvLZEmR0IjPU4piiOz#@bLZgd&i*+c49jNIP=0XMQ4M}qeouK5Y_uYhl$Vgxq%TZyl^Wd4awY>JP!#-`#nIlhZ-miQ;oYf(vSp1!BppL=#VNd&*(9 z5!WX0zHU63gJ4KAIH7%P2TVpJdgogcE>jwu0+A*3T0lQ;m zPUQg6A0K!+QkM{nTh{Xnw;OdLw0>NlOixf2G9-$=KcGtH3JhfeDbt&Yz3jzl0iH*H z+5CY9!pGqCOg^SY)8@j$V~NO2qRCoL>M}?_nE+LLZI)nkfZ-trLXV)KGfs65>B)GSnM(#zJgF)XEH_N%1u1Z=5>rvw|422N z-2|U}4Za-zr^+|I(3s<)!i84;fk*57LGTOXa{js~6zb0x{Kmqt=(I?g^_79t*t5=T>n~W&yCp!GEDSJ}!tAc{_+_c;_M7Q}ceFTj zRFfqMw21ncQIQE}SVmi^p^ODjb0BfWG(r67lMU=IZMYpocQ;c7LugQ4q};Cmb%`q= zkPEY4;fgsc!1FMxeEVT`)-%`WNezT3$VKMD`8SBE>z2QgY(Y0;D(6O@t|AAF^B>}r z4W|nn3A4Z+1xV-Td?jwKKD1-oYkRi;OJLyn8tCdXdYTx%+U?$1hY+P;`284rl|BQS zk}lg;>86<&6r{YUjcCe@JP$dXDc$1=W6#d#)=1pcM4&*WAM^pX~=D z@1u^PT&efL#Z$ia4+pq-^9Q#iIl6ZczhdC${2reoH=P^GFTpx6 z%~aFVFfEN~_+x1qp`f5!v+|sb#AQNxMp~)?Q9^}iAxOC8z>>B^(gb}u@mL}l&3q=& zLq|A{17+yHk?xQDzZ;#P^LKc(=7kL$Iv>#Z$H|ZAvHw*^Rqb>gJXEAvO{L(1bC9q3 z%T~UWG}*>&ypUYg@bD8`4aGN>U-NdAS?Ps z&3F{M=-4`58W{6SJio&7WXIDI>Zie=L#wX9#_tA{vQnaYrwIRW1nvgST4t@>yxf@4 zeN1!9i;feNgzeXpVRfRqI-uNqqKUiukGc}8iLUW}+oK2FtMfa0*Ix8;oyC0*;mo4k z48I>uc79D*3>WW)Wqbn@-t0=aJpTw)FCjeJaQCrq77xEOI_kRg@uPY!EqxFE}Tx z34x=HA->VKT(H)7WT?4@v|*q~+AFx37%3^?2@z^aHlGU}OH$rBhtYI)TOwA{r&jd5 zJ!yZyf&^lk3y8Shj#&_2)8@F85Dr`XjYjdnWO=S{A0Tk`16z}|Al{kQ`IwgD7m!Rc z^9Y7w|CqCZ!Ypd_;d*5HbM}GiSW4+voYp8|$r#k%2PPFFy=n);@PZYaO6W=Sx4&|Z z?MmGcNNzL(y3p?uT|PH?9k5R@8Z4(AiWj8chb@(EGVVMADs)@Mr{#h|d<tZAW*CJtIFSYKyaAUi|yQV5P z>Xsl+`ztqo_E6HQ#JC}g6#2B}*ZG>-tc9s(xJak|=ZHm2j7g3GR3nxaMJJsfj`MsK z|1u6+&XW|n`C~HPx{U#rL{-mc8Qb)|KD-x^_LtU%+T!SPtsYd4z?_J zErr{aiSvfg5}5-k<*pxHq+W{{l(e))XLq84gC)P7DtPhKO{?n#cXQD5!r{-q*3AgW z)eHLn6KIitOh{$$X#{!CsuN($P~qCxSbk^)lvwz%J~vg8Jc?lM;6Sil#-$SA2`Bu< zlU;&!YX`?vqGeon+CHlXJf3pR2n2TfopBYN%;*hqbeiR6d0Z5&pJ@D0v+DlaSIdJ{ zkis5%fb#CQT@33<(ji&9=Jzx*9m+O?{tz;}CmnjR6%+D?$VPTq#?}~cSDN|9aM^Wh z@+*F~et29uEiiDcO!MI%d?d+SJ(GL#Nzj~feb{{4?n{&j1AZh@>i6VoeFUhD#xQlU z^)eZhVtlpsBPBW`L_)V?3eqB_pZQ8fy_^9d5^2QZi#0lao5^TI zV}>kK=h8Tnj&6x$=(V+#BpJM>h$6O55G4jf{+)&)Q40l1LCSZffBSz>O^Ld7i?3E> zN0Zpd`aJBTHoIlArp2mxiR$6wbW$U}<5390jMav6M##EvIZ+AKKyTrS}_8N?$+<4>Hsf6fH{uN+B};*Y^q7Tc#hs0kPuR> z3xvhe#LR=KsaNqCI0ZF8J_DI7;{ zv)9;32|_ALyo~d2E>KvvL)dPv_d%}Dc4_*N5JfiS5D|5NmRz=J?pG^#m}$0t#s+0% za2{65%5>nJ7OD9KapG@Mg~+ZwUlQeLLC^??Y4L8mv2R6^;cl%(N_nU89DRb=-J->x zpoF>MH}~2tKTQ5ebvhbne0_KmxX=vr3T1ls-H~E>|5jj;+?(ZngF9)@YQ?sJC6WdRywX7R7(iH}j1BmQjh3R3c^$k1u~3 zmWDfOrm#ZlE->onHL`P?r#{9dqdSg5jK>*6I*sj_#Pw72io9)MWIrEDoJ`!k{6Ntr zQ8)*ZDo&yUyX*jDkV{$D{W7~TJ3S-tkmc0>XKLXBqd&zn=kY6EfIW@iJnwloQ_nTi0X2wQ@J$U=2=hZ*1F26 zSD8U~sn_)lYiUe>3DXnC`A)QW3xZRoL=br-c9hoFbJLW*)rdCS^q?c*bx$!mk=jrm zWZ}pty6wYBl#X{)apY3p3IujcGE49u&+plwO%Q4T0#!4@(32Wx9BD4@!4@sgCI-K; zGK8CWX5>Qu7Jv~nfC*8v_AQk6MZ^#PshxCJa2kKVLTA>jA<&wr3=@g47Qy#V&exmm z-oq#-C0ec`Km5M79fQqpUa+`Gw69V7d+u6IrC_3MMAOtxN3QCJR85V7dr}(=!)%cu z7>^PM2}|uMg`B5b{?&}WM!RtuVCeT2;V#w<=}cI>?Ak4kODF4Xa5URGk<2ovWVkSw zf;%H5JVofO)rawPDxF7$9VVc2j)*0TH>xbAP~K;eRjD;;CWUN@%OANV zk$kOW*B>U0Q!NzkN2e6Cw9MT#O&@Zmybk@(5ngAO*#u@nD9%J)CxLQ=u6zO48j|k0 z?FXs(bb)a5pLAQy?LD_^q?9Vg&IZvwbz^JEWH~maw<%?p{!hikLXq33C2Pl7zydSW zuZfCq>(+FWm}qR_e%GH!7D=a*cPzP{>dZXrqgE(-*y;c20{)9&n-&b3P*_6?LDA8j z4Wh5$86&>eV2C^l39pxzS=WNi7&JP+6?|@9m~k2jEN7W0kiZ$$CTecqzgtt5pjV1@ z(TEjIU>z4YxR~>kD}MH0C-ip|{XYP?Kt{i|`NSx1J-crZ1N|=hEwo18Y~s#eqSt*O z0CJn==#`6Rg~ld<(LStm2cKN~fnMk&Zy49(br~d@K9e z$Q$ms?6PDEn%O25r>ArRHRWTnktLm?@2<=cPpwRpk6W+0>c0c6E;L1v_b4}&C_tR_ z;QNMi-$g{4=$xQK$2xZkqjCN;R2SVSKZbJhns*)V`IU2JJhDLfo_5AaFBTb(83WEG zXkTP8cXVW2(!2XhmtX$pWycebqDTvog(^3kNy2%fZfVMgX1M@gv%2G6=lUW`vu3>t zxB`~4s;Q&BI4#=?ziYJyI_=IeN3UMpS7z?sLn-5**-K&!4-b<+K4Lmj+6xdyRiuN`7&=8KG%?`;+U|JvE3O#4YyH(< zyYuRgUH+BJKm5i4FMdiq`sl~$WaT}sJKzQnlpk5}MZSvML1~Y&Wl=a4I?w;evOQe* z>1+YikuD?LK|pK6NN&ASh1hJ54qeStNjkRaGgX)W_8nLL&7pU%yZpEAy5{3AeaYhb z5n)k0y|wwFDddXQkTBS#F{rEEl2aDn3?uBJhfX7632My zXW)fYST6vyp(9to3#t(Qc4&A#PeULGJa}}FuzIx-ee}C7_H+Sw`zy8r%*IoJb zTd%(C@~>QT#qs&brlx8HEXPd04$Nbhs&ulkp-Tz~avzq0{M(-F6R^s5)h|*mAw%v@#-`&jirQgU>ZIrhX{n{nkkQ+N>>=q4@HulGmM9&D>lQ9qT9e>Nm-1_jk7^h}a~z8D<>xWH ztc!s_{dWV|%kRDZ%IkLZv#}+QuUq%UNENTBYWI&d52zS}fPCqUjlyVMZo=fGXQbVM zsZ^l-=%QoZeR>TC$ff6+4(&C$ucc9?!g3=-1FK3$&pB6iWjJF>jn9{(5IP761TFw& z1wNR~rzLE?9qWzfv+hJT^$y0UazSQb44t?KwpXe^Rm z2R`}KX^%i5FVmI+A9x0i?Hy-HjFunJ5NN=<+(toVr;)$u7%UMBQ?H!b%PjE{QNiwc zi-YM00St{#mpfU-;TmT#vT4&DRhRwd7p}T|gD4ZL?)1L%lb?L}_A4&C#CORnt42Sd z@&t-vRd9#pc>%z&q;xdJ?5My)#N!iqz87ct+cDF{_LD_nbHRn(9G)17FrWeFqGoMQ zv~)7F6cui0LO-H{Yb>Z&TixMJufOZ&o6i5+D?j?5#4!aEzqY$KY}inIaotDm8XA3Q znL%-F*6HMmIudfhld2L`lzB78phIV^OGBJ9ZNuQ#{{F!SkCHmG1G`N%-+1LBt+hsn zj`B8z{CLgE)vGFf3qLa0>KHNpDHmNsUglgNLq`8V1H5EWKaH@3P80M;;eQvsp<`~n z{`wZ1@6pZa2c-itcWR5=>(WjLLOQc7qk!(B*Z(=rz3%}bH#L@<%tp1w zMcqPkAsg+)=x`jl?|bCo+bewoeM!n_Os?o4hEPgD8O;byfv^%V65D~yL!?b?+ne6H z;Ugazn%bwNM@BZgjD2eIu|)@>pqw@94Jq1Pa4O5MX;PL3nhg#mML^w%NWW}YWGoV) z$VtCb`i$L09yA+g=pfX+I4SvQ$Ruhb#F+}Ya4Xu4&5t~cfo8PfwspJjz|)QTtnZnQ zL_>4Rh{&vSt{j6Su|3KNw1--~KhQxm>!Z(Vz4n%{Qt<_c3H;KDQc6MeK_K7;H#7ig zZ+8N_%imfCFB8;O>!4z~y%;TS-UP4ED^8%M00zF6&5R9-q<5nvXVWgP*G%QT4Rfef^MiMSdn#(1`ri z8VzF$_X_!*4vbPt8w07kwOnRy>lafh9S^fJxo3Bl(|wh~yPerlQv}_3cA~`B< zw>H`|hOZQAjM92aqf)IBGvKsBQdsC9Se$RtYj%ooKNeCAw)RFv-j20f%XXgH+N_csQwOMed+Uvy3?qL5^dH^P^zV>$c%&ger_` zMl|}9Q9g1JIgPTX6&T2Z&$sUh9}8qWLgUKL-Ejq+d+r8%NQje>QQhjcK6&gJtFJqG z^=h6~?Z|_1j&GZvwYwJ}2&*s#36(7Ld}ydr(F#}4I2sy_6jeqWyCcJ&TAYlWykXtC z(QUEp`sF&EK{6CBl>FMZvrRXhFL zYBJ8az!|yr0Xh+aFQg1tDd!Ab~`upJ1fV8c~|;T)myM|37w%jCCR~QA|Xg*Ec88$Y~BpR$c9%WzwC`}e$5>> z+^}g2lHE*r#)?HKX;l-{C?UGUBhp$X_^^Z>?>la}Wi0J>-kkFsQjuC&ycqlBVwQ7p z3@FnHBi=Ps>NSWn(vD+aHU$BlpO{X(rv3<|)FtEl<>FUmnWZA7z0MyK|Mzz66_jF2 zR%z;=hD4_{=PF@z9_2MXt!2?Pk8srgT+(R`CUhi2Zd3%1Y+n5+L{zCzM=TPC>%yq| z>u)^gf?t~k#k5(gSFiR*d+D3#>oS^LFJ(&PII32mIghQeHtj`<#a#Teq&?eT{@A9k zTj}_RO5KxZ2{)eo5PMS;1?@+{quDN$Qt)VlHV=;>sP@hLbAa1m1%jyZ#xX`oBwOsA z=ZD;uD%z0*eVofe=FVp5t5)v0@5(FNQg3JQ%H^j&O^4NG!VBd`sL|OL`>B*F`QjT7 zJ@)6pJ@WD=9lp(R|IYAkUsheb;N$JF)=-7>CS@L-*PM|>*+_{U%D5Z#!U}ph4omwM z?DS{r00)t2Er=!{piGF2%Q`?2h5X4CXLFauy)*Cn%niSssJ6@3o!4DAG`4xuN^Uw+ zv17x7gYZ1BoU>}Rjwq_KcMa|N;4vvk^OI*nTF|C|fe23eL>1Z#QaARgoa^Q3Uj;NMzc{tF~+SvYfj!LEMI=e$!D&9--#>N{OQRn&-v41 z&RTtm4x<<5s>D25?SOf2Pn8IgrSnm%)x=-pB9}tZ$b?i@QQ{<~VQdbRjzpTK5a99; zDB+o+;Q3_~NEWVMFF__wo*$Gv6C*WBLnwwkAigLFK|d0nANbY&{4KR($fa z5r)pC$sgW&_0|8hhonQsz3^#^FX;?z`caiSB6F*hqCIuwk1CBvK}9e;qM-AKpk6=b zn76#;NxSaP?oh?}9`d4~QO{72{>ciboO90WO|^D&P_6l6oi5~O9Hq7FQbl%D;8A8W zcyKU9hKA9@HKUF$`96tc58isa-;&iF=QtXUUu|OpwpWAkYJA>x)B1lju6UozlEUNM z1mvM3o|tl7;X+K`5bbu0PEZZa{w6Z&iWuY|2q+IgRyY&{rwlwpNo6F~EAp@2zW%bm zCi-b_k*|XFAO=3qA-JZkIEzgA@c`2PnOYRVGI}kzv<5uiN2k*%=U+Cn?OyElYLE!Xrs~KhB|>V9%4H4)Me#|)sv?|F-?};k|%hTXAp)VHlmB)os!O7$@>Ote?sw|m$t7`Rd8`VF6YTuYvpGIJk4WUOVsG=7e};Ccr6o&;maBksDDv$T&6Am1!^FG?9G z8ky43;qJo1gYhdUm@b_$F8cDeP~d{XvXJ!yIJPSnd(HNKfPxF6%`6Q6$(-d{BV}2v z%&YB!LoeUe@ck!0`D9ZG4^K!}K^l$pOGWcNjilQ_B_drK2#^;pjaT1s*=1w9!nv#R zFJFKC7j)q^aImn9mXTbD(gie@d1l#MLsMR8f8jj;)a5(*q534Y7i!8wMm_}oxV#w{ z+CieB@z9h<`I(`#-Tv;Yo^0T%~u60fChsDL^`207{w^5mzz zWK{kOHbF5h6jU(jL-;Ed!{D&*wdep{M`W=_SE`Wl6^+T zEey3Rl(cL{gfEJkO_M@BCN0(;wd|6e%q8un=mvNxrJ$5z3Z-pUN)lgna5jFh`?NWFIm-TLtjd@wP zhmSkBrp0-Bw?x|u&ZY2Ge*Ri1McbZ}H-_tA734D+V_vg|-OOn-vSm*3!4`#b6Id+; z#i@x8b|WbfU?#_JWl^bC(c=-D{K#{y8enAeqX@JDl@ry#XWF302!-sZ0!F7goesok za+3wnFo1nv;?i913?-tV6FQMqzEW#cu2~WWP+kNts=yhIyeP`@=^m=g9DkO{{r#Xa zf@3RaJ;Y#1SXnCv6lq$~DlvnA%ck;MH=n*>IoMDeViF-8oiLX@= zP!J=n;dq=9GeS}$KN2p0N-0Q$OK~BUE}T_>=cAP*(A65+g+1%|(^nn2GZv`8JJD$_ zzgj^IgVdcg5{1op)H#NVZ;*G}|8wtk*L+pV=K}m9|FGifoaZ?>G=!ng%|Z%a7Ei~; zpZ3FWEMcu4^l~@t7*1Ka@<~Bdc^1fT*{KOGp-m#DF}+8M6g+xENo&+K!r-s=CSo9X zC*gC~Uh(h9(yt0`=_6o-D$6oR|GqJ-rr}ZMM;M_EtF@!ClPk+Cj8FOITo#mT!6W=H zn<$S%Q{aMVj=%W5M;6-rUFsQR+Jl3Fa%HXZAxRNEIXaT!-hn)FWm;&oU5psX;* zk9wm9ar!dKr0_c+4}|_WONEpfdy_d(dFM1=sRyzoMwXQy*y5UCEOY&gnj!bo_a4X8ssX1hA)(oszzQ3A_aGIqWw%epjgj9@LTKl71Y{2#}_GDbSAGDm~+56-JNAfyE% z0|JV+G3~YxD%~~6Zje6q6eNP_73r;p`ZZN{L|#OW9T8ilDO(Om?57L*=DfY+GW*FKWS!Xjw%+n(8 zXZ30Cf}0i3^U!LwrY)bd%)&WN648lf@J0Ucxx-y~bmj=4Q~F2(Wl=%+IoViGj6Wx8 zZW3J7*ZBHm+1|Xh_Wy+uXP42Am(Dx~P>{E>9*J!eryI~bmB7(^)6ZPBEj3}TkWJzt zPo3i+C%@%QKFEt;rvPZ6JFMx@YmdO=q|`$n$a)A#S#NZtRD4ZGuShc*155j+N<)E( zON3Z(D;j-yGuxTeb9`^PL(j8W;mV_*P8{OJ6b z*Vlcl_R2&0s%dX%Va25{L)7G326DQcv*odcE?u5vx3kQZyV@&s#5La|O3vciBJ1-Ohhi zqmX7TgQ5%xh(XhI0m86?gvV^{PLJd8es#KjS?^b@g5OQLbc6#~YY{R6(V>%2B@aY_ zJrtm9ZaysQ?Do(7P1omSUg+!VY@XSKkr~5~ieUs!WEzxtk>@nAXz+NS(b}i5jUr5Y z`@ZjD%67=;r-ehcR)JwO(J2dwEI}DStCu;WZ!L@oGUL$!)DRit3`3J|@}5L43dAxF zJzw?J^;h3FnLihA;$^?2lC%lWGdgENw>WWGlEX6r4C&idU-Gkv!<#q1wHo+c@(`U) zi;llR6#DQLXippp^2AZ@WNhUovsQy4WBclKG`iiaXtmyZ@3q(d{jT}~v@7RjN<|rm z;04D~zF9`uQGCtjcB;tM_md7qdV0MMvOGbp&UrHiax7>35Z$6Mx$#%*3Sob)Fs-;UD+pktoX&Wp_x+aWkaF2sf;opup&xb%Z<)c3upap zNwyfDCrn%#xzdj=f7c(q=A?5kIBNNY?>_SNgx6j0&ZAB`_oAat;`_L1;G_%QbJQto z-hK4z&V3K-z2~SCnI3E`K5AgW!bgnnL4M}4p#6}jdcjSpn&^$agYu%3RnMNt+R005 zj~8!oDuthP%=-Hpg?{*FKN&CG`BUa(`x@2Fo5wgu6cYBK2on`F?T$DnRQPgj^u0h& z6z%YeqFR|(r|g!AZ68~@wSGQ1{B3K@_KWf$b0&n8um$s01RfpH-cJ52I1BY8s|~Eu zD4fD13aT8OGSIbtWKn-QoQDgJyXcROI)*ZE?D_9}^~ro6v+}$nk3a9iBTpujGO>+Z z$lPCi^eJoB9KHO43tn^5+O@}=c;VZR^(qxpsn(p&V295j1Ykja^F>?4wKeDB7=#hk z!h6QnQ9O1jJ)5qnh`T+J+cvOeH{zsCcF4J4p0zY0NyQ+k2Y%8W8A?JFF^>p)w%6%| z%C>AAcWPl!Fu=tHJ}-a3JEK7t?^-GLQVOv!ndruEW{xK&pY3+rsv;dt+hap6OGfFQ zcO7?W^=fUh$^r4Vq})+lK(G|p@?AJCSQ_%1yrEI*j`1C~I8HeYycaQI z>IE-3<%~5m?piYIjxc@zOXDX-+d5VLfPFxRWNd5<_EtL%Fox;D;Vpv;n8Ue0}nZ!Pmf2HMlnsJ;RR9swQpUu zWTuY0tRK+HmWZwxaWUpH7qG50=+t2pgJ_X%2ZHw`r0b~#3*+T2Wg$q$SF3f6BC&%Zk!mnN5_ka5O zZ|8CMuNt+;`JRE+3O&vh`!40VS?}Yx3zWQ|t>|{!jJPG_Z=2`e`EB>!aKkSCo5OJ$ z6!}$x)yxp!YcFpuvA2v44UT!*Ko$#ee8h;eyg*4~p9SAV`7Mt) z&!c9of5s@_AxjT`W1OUZ5Cn*#fOL1Dlp-F-xzzmb!Pf?Kd%C{?*Z3-4QGQjt+va zw3R^gl@6k>w4HZHOY-2~5yQI^CcG=E_uozj_L*^(g2dC_cu=t1GB^SV3T1z^sZN<0 zbr=qSa&kj1kzwQerEW+>NhyUij>k6a#m`Q&ttH-v1iwJ&`K8k;5$G&SApIEMLyy4$ zDW49;Mn|YmuAG}`%GG3^Z=Ib?PkpohEp?{mZ=cqzwRO`qiE8paURxaG#huQ!nd3#{ zPpu9Y-n;Ry@~P=eYndN@jkelI3m01Le?HdUJ&E(~wSMhxo-=+miyB|;ajs>)J39Pw zOThYH?%MFnJ?DM7r-Co{GIM*kP`Bq+e}$Xs+grWlPrFI16UstPehU2}+rn3|b)uiG zPLG;s7qa`u{MNIfKq*S)Y$|T8;~b4G2;%f{9;pOd3nd?%041iW+kVGAmw&i&`$zvg zy6=W{(XCfrR=I2ay6PR*UR8hTp^N$#j6Bqrb~_jKRjbzXGzB!F@L4$H=+VG?qdtTapN`p!$q$@Z?&(8c?_!Dc%8@cX{=S` z$ref+$0A_))}BF<#%NS3ptI)Yip~w!UHQMw?%_vA%3j%}gO>HWG`OI#X+d^&9v2?Y zJH!}_b$dvh^9$d+W4ivAFJDd>kCw)1jvxqmG|*u+jcUC~K`#(htBAW@3^W=){lX_c zG04I_zST4>jn?0-RwGzCalTQMHOnA!49^&dLyZ5Cj~9M_I^OS;Nx?Rq|zI^WYc%afZ#;M0c3HoL0|B z@(hX_InDJ!!9su(v5F;(-%dgGEgUdlpVS>ytv5V66dpIiUNwq*qqOfE9gqfs3XD;K z_LNV&zVfxtw28A^wK`qTatt-vJHtM69{(k@nH4|4I4^`k4X1sg+Hg}%Yyrp@T@?}ss_%n!(uGBM4g-q#Q7 z9CfKSP3hLGOp_^FKewcm2>h0>nG^IOw3LqXP*OVrCkUHrwX)v!rOTI}Uw6=b7My+Z zUih!Txu5~$Y(P;q`nmD;**fiYgYwkC)1rf>C<;HI3}`P11(Y$7_5@U9JQZn!2wI0h zwH{bg@l!|32H$vIsH3P#k)r;RN32`^_)}QnT0&>O%?N#^R)z9|2I(bLd_IOZhd#Hx zYh%daX>gM1hr^rBE zelwS4g$4?R@{%&oqI3eaqq-@O55@%5zH8{@o3hdzJlMqq7K@w_SNR!H&nF?|yHOsu zg$25dXiikx`Aebvt?Gtxrz&kXMvQ_gj-8QCIBPho9(fa$E`a9|Hys0)cjG`8Z@u;U zJ^5RPnhOU&(Sl{)J11_}r6VJw>kE1vyt%o}D5o_jrO@ki;d9=h3_>1TH=6yYVTLQS zYLzlN1m~7A3W?@>94luW9%a8M3d%ypdCjg7n>quU-Og@=;inm8D*z49zAFk)`E$w` zzm!s(U&{zf{-W1T^M3>X!!I^Im5bqT$}j#Uv=wsv8~Q$kUY~E`E)n(G9Xheq&z2vv zcqpTup7JPJZ0FSK5{*%l86w}LduXhMm@<*l83?#>l0Kz>qc|o;U}e8z$X9-`wq@&a zuqubs7L|Gpm1-R!7yWJ$mkxJG8x(~;qjf#DaoE~tnVQxC{zCA8!*TPS{Ky)JkPiCP$_lu^%sc3*2< z@{%`_eK3IMIzGHv}xl;|P<&Q)D@h zHUpMURGg#N9d}%o6y0=%D{|LnL^Nd-EiF={G{~VJ0CeQU5Vd12NR>+S@T7S0_FZSd z)2&l}VI7T@q7&j^X+SmGYZ~%yoN^=zp7!?I0o=ZC`CeS93{si%k_M@r-G1rfwSs`vMmvR94F=+t&| zmUj7IlEu)*z@sBxDy4OR1$@sNHzq-(q4l$N=yM{Y)$O<*ohGFWeDXX?Q-~9&=#U#I zcuvvK0ftdN4O9LDR{_VFu}Xthj67qBl(TC^0LvU{>Sfsri$jjM7;|uCTX0zE9)9S- zBV=@V`?(=Y8Ko2Fr4rS))ZA&Jq!YOH`s*LEX|`f;WH51x!%;r1i-FQU9sdHBNMiE1 z8ih6<8-3TEH>~?1vu1xg4MmvFUQCxaTf|?#dEF0^H2$By{zmCMNV^~ipeckY=MY9y zp9&0(j=t~>E7v?-%BKP&I@&7pmcXa1($OpE{3|-rBJU#iUKpSye{0gYcH*`6>X$#J z_A6;KG<3hU?iU3YRpGDX321U#cp{)E&jCH6qDW7XvZ-MF;Pr8q`tq~3+5nA41LAxo zbVLkg)+kMzVL*9)Xym{DeOABt06?Wldc@L23%QX|Fnb`Q>@VqK?{LfHu8SG8J|ji)$%Fw{ljoWL|*4vrAJaj_q*K$n)X3|^k` z7+1+ibB{JIL&Ob~ID~Ry!s2P~fIO>IYAn-)8E<9%oP(LMe3u29l(Lc|%y<`Y=(SqY z_LC_*i49A8@3yNkE~5^<(WQK(T;J-AD#Qtt({k63=7x(EF}qTcUQ_ z!U+S)udNSDw)2aN)~Z2rULf#vX^=HXEi|;oIvP*?xreD2 zIi}#8{E|&ZMHXILE(S25G^6|^)|mJ75~fSrJnYD*zwKG8xJC{Wf|1KaCr zMhDWb)KoVw-haxPjX7=>?biLZYp=b- zhq}@wnUndR+L)q%DxK-UM;`pb*FSONl~c=RmnQSdMbiQ$uQ27(Wms{X0}B>~6w=k(qyDD{Hy@S5EwNBs$Q_!s2Bld+952Vo z&3JM^rx(f!t<<5EfAg?D&HE{~v##0DG zA3bisa!#5t!DN*k-j+dvPOD8jueX#5y^eS-M<|)1<$5n8faC-e1hXYd4Z&^M2S2vl znsd6i^AxGDC2um3+guq+{^YmNH=Z7^t`vKq4a|}h&1MrZqwY$n3CB&*2^1N0ltTy0 zdIFA3tN;C!f5!dfe*OzkB4l7rgQ{ z=d69zap%7CsGd`Yih}}MP(&=n*7EI0kv9%y^P+ri^Wfxf^Sw78>`t0z@Jj(xq{ZXH z=aXj&7LGzk8;+FGsEj{D_dp2*&DhtWa*L6lCq2{307m;|z*C zS3wmmMwT?`_Z46)%Ec&!K5f}0mvDx*G)3&26*lx3pwb9=3T(MYDRM=6KpigNd!)6X zE0Sf`Z*4VqDVKLX9ch(onN|v|ufkG>9feC|$Z8)Z;^KOKX`EHuvFo< zAl!3+*g4Gsd!EMyvw z(`-wj;S(i43euYk1u}NUv2hI8dXZsUKeRS3@;E_sa%5ww*v?W99C_{dwyf>h9Kl{j z0E;9hCR=t~FtwHc9(zHl7fLB?s{LGsQ`VdMNL~Jr0;hnA$)$D6nox}*B(g4W7D1Yo z8Ru`v$Q>&&Fcc8KWil^$1ImTd29_W+AuO~XEZx3Wmvw&1olUAa@2!zh1VI4bXcT!0;{Zox z=zem~@VEUBMUyrA)bKXkR1FQdr8mEN)S)ikOR?{E&L6l&x5`q7erF z2$v({(Z33Sgl~^VD<)1)`;h<2hysE7q9w;HJN1kif1?w-ap{R!5Z>l=uoH29lq&h| zaf8<>f2(^p5KdIRT(c)3jTkp8#ma&PdyZJI0*FOuV zEN$0hb6qCJ%K)Xvmv8LG^~D=+93k7kR1GMnv|yA+R5(vrk#jyZJnB+L;mjNk=^yHD z`CFFDSN}m@VQ@;JP+rP;d}}DBAPyK*j+{DOB=OH)a>NmH@^{YI!8Z3xpZw$>wOb?q zXPiOSOOY~S+1J+xu8l}3M~aDFRv?23{d#4~FS595_@+W@kE&YgCP2EKgPp`q9)t5& z4TE3aapU^k`Cm(1H3)toCjoMlDK<=SkE7LaoG8f?pn=MJqr)xR>x_6sHat4Cafq(p zP#$-NI;~-WLB0pOV}pd@LDrpsQNBk9yf z{aD%tX{OvbE~lpU2UrF6>P>bmRi~!GohYGz(i)CYTB#u5*v=|G_@N-UW2W}}ZZvC7 zvuD8zurJbS`QCz&GBQcIooAdGyf-H+P3NZt4yXZlV%wa!InO*BaTBw_BUKLz_3nql24lx4S8AjcwZW z;4e4(Me&c)_hSuH`8OxD=rna+p`Eb`#%dTxkoN>=c{9)B+q1M&MJXxR*=1NLc%+TI zC-G|Hu@nj#k`hVb6!&zrBCJ34E|lW(r!+64jSpowY;9;>D5hGYVA?8O;vm@*O*NH3 zlH16XWb9W^O8OUND!X;%b00jo=&HQ1=yl>U+91aQfJV-Rhx>5Un+@{I!85|VZ3(z% z!-i2;WL@7ksMTta1-&Q=7-LFcLv+SE@cp21)T&i8{~^?E(a!p-+1LNd4#W4ha0uu; zfF$Nj!*FLu=TjB~O(F6vh-Oe#KvWM%(NRh7NN-(CGBK&}3A>`D@ne2)&TL zUqD^Z7-_eC6@?!;e(l;O_Uh7GTGOvB$1AiO%8T$@3d1mx*RDbBSmmyp*Z(x{cFxVa zW4%xnh>YuI?e@FA{kNa|=N)U$ZH}|_zV`rScXZ^sJY!rUjp1pHE+gh)7*hWn#wd?f z9_{*3WJbnDS5Bgj&G4I*QZ=K-nIOi&F}c--0g>m!cj{U${C2Y4T)qWwUp2bL+7y*) zfLf!D5pEX61_ac<3jl}Oml@n z@Y8PGb18RaC87HzkP6XnlA0!IO89H4d}GxNeXcqXxHG8_B^KyDFfN{a{REw z4@QJSt&-8rk6v=`wI91uNcz%cfBhexD*np30?N~@5|{GUq2rP9D6+57kBszKIwqAy z^9aQGUr4(>0XnDKD3m3!%qtyLNelgsI|j%8W`%znY+GC0b;Ff^7a;r0@S_iQ1DD-; z=grq%yR~%RrPLlo0EL>nKYjBz3r3>*Y84cDLY)DGA5zxrAHdky81llQ*}w30$DID| zn&fL8oTXs^NByFbTFQ%MRKyWL(6k*{uiLHqYEFOoL;9WyW1EIPZSy2{oVStDA#Pei zRKp0CI!N8Z;NW1nfy{xIEPuy4794rrd58Hy_(G-BR5aV9HNUhiih8xafyv#vwb4KI zc}yAj{&ww%mApb}*AZ%Kmu=Xvp_l}5W+jbFFSP=f;!-PllR#wU-(HpXG{CI#7Kz*j zq&-U}+cG)*O{YuRObJ>mEf{kKBDFIhNa)rIix=b_AnZ)G9+{xtlyU1-u z(*id!f6~Sf1Gx)O?gSJ+xEAcy-@5B-4jf{M7rdG=XpDg^@}<>KA1ZTy-=&x4@}2~d0|A-C$vk;Wo`B@boBat5 zJAjl6Osyl|_uY3X)q$-Zg z*=32`Rvyy=$tNyFD232!${tZ}S69<0YwH|e(3X7>p*X0D#?z5TD^O-@fa!5ya{t~= zx^qgNTS3M}E`kHYzPz}=|F-sgwA)>1Ds~uDN+&|->18r+@n4|m;7f8MsKNGQ`*8-J{4-&U_Q-;!koG9LTO_yh_^!Z}u@ z9JJ`HwNSbJ_m>>L1EOlC}FTl*hG0RI$Q?J`A4gv9x&*3x`nB|eqq|;GMPJ8z^>yH`^C1+&4!u2Vo zTnhL+DH19(t{Ig|#lUut+77{tIJc{JP=As&`+$C}l;gw*g9|EL3GB%-&ylSZ5m>(2 zvy=IyU-Yg?(_HwEFztRYCB~R=0y%r3%Xx z$I$@GaiS#^&}2Tr7=z85H$FTa38Am=ppv;fXA~;(;nDcZ(WSKMbUHAc`y|Q8Q}e9j zR-Sk4bU5}TFGK!zrF9wkQp%t;(uUR^>WwCPNsPi-c)kW~k%Y>>Yq~yeziIssmBWML zERD5W@B#x_d=)Eaal$81uQkDlrph!f1|UV)+w1jbpZz=&Mz2=1P{OAkkKcQ}gkz~d zeiJ*T!i^=IJ9BWzh%L0u?v2jly2R2s|OuE#zi&lEPzKKZ=Gi{DOM zjw8+ivTA{<`b#pUwXtyH_1eX{^NSn({qkavScC*Qx}# z!KQIs#9hQZz7!pzO8}7xXb+9PfpVkZg|%~3P+t?LmRm9f&G`m2bqf<ODd$o)BA}W$+7Y8QBQ6;jf=ATsKXtT|ye;77uwEbk%~njg z6!{{ZYwMi0`DY|rGNpJ{$rl}Q$hT{?>PE*+(bNWxJfb>~Dy{&=qn*$LKl;T(bN*u; zQ~TVVG?y-pekU!2F*_-qP05iCfNuwa0tYsyacr9&m%m`r3@q_a! z&#fS`GrVcjz11kN8EKUt>8VAP@p?u>PIdgiN5ZnOvctx{#JLsQ<1;lr`*S$9Tx)H$ z5{C^U2qkC*I&k3P3HeQ+kFP(RBUTJpH86 zcG%gB3s#5GE5CH2;52I0266iI!2qj`&OPtC*Ij&Z|8#hEC-1}+XD>HV<;_W!@~E@` zcXD)|LUemQ%0L1y^bvD0^*qfer2X9I*I)JkroUXT-MTurGzr?l4+Cgp;CTe?L4LD2 z>7{fgSr*h@{Hm2_zZZLX9lv&MSoqaX7Djm@kDC2RRx5d7XxLIlU5dgn5?IKOMj+{SzFlh#trEkyUGwtu8=v^p*VES6 zCv4h-EY8$A zkL^~g6!f%d={4vUv5p&}QFkS$k5sPlOiykAeW1a3xf@RX{rPv zjkJZPE`cWuy*z)Z4uap#3Jdw!T%`=0qA{mko}^RLanrWQc<UeHbV|}v)j@^M_b(S4DBpVtYWcYk&TIY;cdcJPUeQ9x=*>OXYjAV464Q_@UOwa31)ErFsikrYy!e?x4U8%2SU$ zd-aLT-mUxiRjZCrVe~GTkaGsOiPl1w=IqH^K(fVNRA&00Kv-eJjaNPr7<}FcnUt+| zYZN}6zKFU+lZJvs8$STF7ZI-7+rY$;XTJFv-lD~y+uZH_@KxuYbHni~S3Z|y%=x_| z!bvXeShBMmVav|Sce^YI3dQ)FvuO_znT{vHBw%Cd_Lk5pq)Z7&|%X?)TO(G2ui@_16 zk3xcgtuz-r0hc7S7|w0QvJX=(bQIzwL0F04 zd0y$bl5t4r2#GUVBwbW|{hcpdf7woc161J)S6ngZY4u5xjx>$YY}WZs0P-O6d}!_% zjn$MnAIb|V9oV~Hch=ftrX_GEnOqoxlg@wFUv{l|mkO#-G(Ix7BC-O~RB?xD>HKMV zr)jFPc&E`nX=Ai0$F8*}pU}pjpfR_kUz{t+)QSU;#I(T4V*VFzym16+{0>*-nerX9 zr%NZE63IoG-%f)lQIOY#@6}?f*0I@mVx&^`GnX`xv_@^YFYdG9GS+UNB;4g^G3qz!I;$@?tDUYel zE$3Y3Lh1-V0gHkg7{^R*Xt}qDZ|b9S&%Wzgkcu>5&_i2KZ#pxLrB?@ek5Ps zQYLAlQ@G?3#i*uYddvO7BbNS0EsBN;u3JG6ATM0GffX7{{!%`BSq|k#+8O_v6W470 z`;S|Y%#e7&>eaqd)$6SvEX+x#RT{Expp+Vbt^+hsz9lX6_FAoN=)-oGPK#^ORx7Uf zJ_6b^p;I=S#`q|lDZzM*RO{dno47B(ZpDf{I-Oenj(1c#e&th%^_E1n1%0~Oe`d?8 ze)r@H-}R}Z-g?H~1Przt(cFCsFXs*$F>$?e$>?y~OVh#D=8cN`7vJRL=vq&uT)F3Uuax5cMRAs0an!w}`qc4?)SU2@6z3cqdT z$*;+APy43xVNO_a{vju=y66Ni^M0hN%|C9%sps=}DWC%-Q<27W+@lCN9c_sK$s&|= zI!N2?ktolvp6qDyO}Z!h!k5>r`$gob4M}GV1I-45R~Zd$206O&qYy#l14biebSj}E z;Z>?{ZtB`UA9?zUlZj6ec6uLk`nzkdI(_AP`W7GY4>9ypTiqBVV{HiSW9=>+G5Az+ zSp@rP5x~{BVsy$m{mY#q#`r>&#qEzNhA}*DEJnu$5rl>dm!rW^U<%>@DyQMJL80}M zIP|~##x)oH_3_JBF2*dblg?ZF;`ZVvf1_vZyP{^l>d}~I^7ED?yq9EjtStFepb`bp zd4f8Pt7r2c-g*7H`(}ZDr&W{#*H(ttC@afTdJAE{=wdxGci7yU<)P>$M8Rf!ao;jsO4<07*naRPvp> z>+b8Xe`woQGkv+43U({sH^JvdH*ZcHPqAM8!$s!(-*IQHe(y1-pI)2V zcAE6GlOXm?oWX)T^*Gm27*5l4_F0n=z{$?!M-5_bmMu5vlYNK-n5F4B(n~Lu-(H@q zUgpUWd)#T?DI3&Qs|C$Xe&9!NWU8zgIc-|C(L{$E^GdDn@Lo6n!Rybz=r}f}K=3)k zy==vbryddZeY^1d*T~$&j2>_8*m9;UTUy#NzRH_&4s;50T*1EIVT>9dAZ2+za zXwN+rR{NSGz3eSPeZl=FoWAOdr@ZB~S^r!l(eCqG?@kZo+b*l$cgrmg=m&p($nPG$ zXvz0(Ubp01*I%*pA3lBkl5gE~^^$wnU$N-hpSpg*w{E$v|K&$4TDUmr9D2`ZKfBAn zP=DWNK6AxA*I&2rTc5mX$(`3+w&Yu%xNh;+ZdkYYn>VaqbpQHw3%`El#|M7!nH&1Q zb<6t2_uX{;((m8$`E7pI>-P1Z`o|M~^*IZVKH~5t_pSfWYT>u8y==kXe`5W> z*RJ`|zimlv;n$8>yy$nH`P2(`Ip8^}U={U8aJlg* z;Wv$xrZZ4OLFEsr_&fr7dUI&_>u*?d;rHHj(R;ps;+l)UchcJ5|L#c_5#+u0_rG(} z+IJJ)bN@+e-&4L{|BgTY&N1h{%Eo|05`js1Rd_+i`3N#jO>UYuIe6h;XA7^nFmJXOt zhb*9BwZc#ivs`cM`>^W=y^);*3oOb-`wa*Yol{iJ*N{9E@ zweNiX@fW=P&+=;Wv*yyn{$kTu%YeK03XM#o(V$V!#(!qbIi?e?jyaM}8$KNg+Zns@ z?i<&C1+%#BTX*Asd3pXWV~g11xIOL`$D7&mB~BD*hQK+p+DGbXe;E3m`p|&~UcKtv z^~ari!OLEI)_WSVFtCAIzI?fP?S&T}w(N|RC%xvJ3%;It;de*7-IrGCeKd|Vu#DC< zC+L@BRxhA~%mp*06JDjUk4J`wkfic=-?rf~aqr}wpp`W!Ql!>Gkq?S4gPH-$blG9( zofrA4=X+qd-7DI7Xr}oSweZVP1`0LUHWRMa`RKGMBn=Sx^?9*o0ZMW zn!EqF>xT7Ti!`oMHh1mT2rM@;lla8(Oc~W@1RLT6=3Y?o>dil^4R-H8YUR0a7Tukx z#}}+#?H_gKIj8>9Q0E&~1%DZYjbQUo8^*6edo^0R5X^`2DxT5N;l8O{;^q1aGvS-e z=aJK7a?3lPh?H8+v!usbtu1AHlMp^Ar65kFPa7>do%6&Y%QGm&e9Z?isi%|gEnU`( z`C{nlOzf}L9%7!fWOS|tk1>X8N)N^h(4}lfjs9o08)gaqbgRH7}= zD(uT*WnPA;`uxK8YNl3I&8185=-4Qf&s`f8$!MRBQ-)F*LZ3#AQ$a;*kF`)FF}%Y5 zy!h!)cM(M!C;iUrHVoCFmeUDrhemTZ-+`s^l4wRi1rw!{U7}J7+$h5HD^L-kQvEgS zS8h^``hO1}_SEhx)?D~cuQ-40f2!ejhY_vYimIC|;|v{4Wz zFlJd%s|QICp54~wzIHv^{Edx6KYPVF=YFq!*pnXVS?_0Fqwh`rHuM`bo`8Rut_fuRDRlBH?I3HlCsO&aUQp=M4{Ox!*27ntB3Gs}lg+~H_RX{=K-uPa*Xa~ka zow`)>{Re(~>C#zkdWd#T?;?voT`;UI&P0c{gH)FIIi;n9lH~GR^hbW7FQmPZAGwU^ z_!(o6C8G1rXaql~&<% zA$G1$eh{F|6Q!7QmKMNpJ6=>8O{b==KwsGb0t2j?edoaGwol1f7Sb_B< zVi(X5_j(=3bSzrX593@1d3>7uNsTE~?KJu2>#zT3ofoHPoo+hN--K^e8C9$?qL`;K z(-}f4gt9=&U3ki)m~NKAuU7T&SjQxV3rtjheo*Uswl#jB!-~#W$AWmsr98K2Fme_J zA@a0<@B0t~)ar~OrW4g^k3kpdFDpss_4i$RDK&kkdC*yY_@{BIu( zmHW58dI*t+4xQ|b5n@@480kTl7>KRwYY$&R~mP_X-9vytTG1=1{u=G;EYhI zk>O1n5oiNLTd9@87&o{*o>T}8Vnc)?MZ1(a_=OidQw8BEh4#*h8VmlYo8}i6#ygXd zspE5vC6#*J6db{$n>N95kTpid>Ab?%3I+~2j~gQ&@aT9B)T8^q@QE9~j9pxgW`IAmj2(jL1i&6bx2(o!K2+%h(uU;;0}5k2MXDkr4?@l*=NYL}Dz>_od*M+GPQ2A`4qT-C>Yu2f zx~9**b9@2kzv%YJj|jcAhoBMBPFmze)@fFv_fRH<{Mivb zO)WJ|-kxU7o_l-R!xOZfQp(MC=vwDae2i`CyYZ4jqMrj*#*~Dpo{+ zk`_{rAoZsLm{$V0D~myT5K<|{X%gj^Bgb*a!-FWSZxl~eeMZ%~MCu@QHgDeC$>Z$J zcieQ{{mk60`?htLe=+pEE5?R~QcZ@~reL7RS;aT$vW~hK~8@ zNC$pUMNEShMs>tJx=F^MQW-a_hG~fq$l@(?qLhL*z`#JC8{WL}wwFBR(Eks+b&0%b zz!7LPQGy_#)m5_=MQN}F?z{B9+$FuUhle&MCQt~tqB(6#zK7aFU;@${g2P4z?Po=C z<+2q!|DRHFUAV$=sbeonDL!|+g4Ux}y%9oANn*|w>QsN}V zxtyF=WlMtMI7YQm1=R>SqsYj!4tlK}epXjn?+*~iAkWjU_+bc%0t)TYZ#k#@&;&_K zo`4@16gGqB=~A!5F#Osbw_G+RWpf3$uV4S4-I1a9ENC`*0iDvEwxh3Fhv;23sz6M& z@KGENL%meS0|`P716kZt>(IAw5u7&Yq$#S+J`B-$^k~a!eSL^(A-X*}K6E6a(7+Wj zLeFs%nqX|`(SJ?H+AC)}mI>7$P2(+m-$z2HUg(qGmhW}NK985p#nb0leF{&?ja}h# z_@Rd&2rxD>x=ZvnZoPl=rbqrW^cB~bHZpGR>*R&AIka-LiTlhc6aWAa07*naRDjkV z8hry88sU_BK04eqX5?#1>@@9Qzzu!P1+6bdzN>5D8E=a`>%1ggV6W6?kKj*Q6t?+mckh0+T?MEGGe*r1eH2W)POP- z(3HcOKn0a5j9&o*-H7PecIY(A2~PjQJ|uY;Y1-w6I6ybDJQ8dPK6fwzhHn915l^0B0e5O4(>Csb5(-D<3h2lu$Cc2hwib|`7cD-Wx*Q%II_Xyo z9H3E+Y8V?Fg-IMjI#w0yL7=W9Xl@?`lsLcyZ2-_YOJxD%(N00y6OOahYWoH%v z`m$;N({;@9`s$5W|4e!Lky(-dN5-RqoOVXQ$^2MFdL(TskEwK{5zzGta!{3ko@ zE29`p3j0ng9$fOL-O zsMMxb+5PmTml7=zv!zlLYHplmyfU7E4lN705g*0Ug$r0?=l31A+%gsf-nj{Px6MYK zP9WD7+RD(^wC%d&WtYyB*fu|iV0hdf(oUq-0e%2RTA@52jQO&zMMZ2TW!UmmsLiNh z;2Fw-73)dBHdmnlr!=gByYI#u<82Ce@^SyI*SCsp=j6cCf1lD}?Y73?8KB5hh+Jev zu4J7J>Ey*c2JfYrOG%HTi~{V>${eSZ0R`W145YUjgoucw$CEQ17^HzkPp3$|6vOS! z;80`O;;+7W^ZLKu329}KQlcScl6}ZxGoId8Pra`Y&*R+<4y1sBb%q8&5vERxC}e}j zotia9hR0A)COi{Bj6P_*6hMapbOdz?5LF>4%N7JsQzAda=gG^5aZs8Aw(Dvt+8 z6giERkAl&IP$|@XzZiYwk$((Q_u{YJaQO|`i%XoCTQC3f_t!jgvW?@1eCLpKx}{NT zG-)uXfHFRZnOJ1VVf8aRm*2v9?j@o2q|Mos9G z@1%6{EFI{SM+!}5k$O6;V!VCdhkz#EK|GcYYlG)goxw-xOr~Sa&^zNB*Ic&xj>|4f zW<}sOmB?IhL8G%E*<=r_7%a}SqwIiRlE3@l5I_3ppEg4Or@msE^Cx2nxwwn1G9^Nb z;n5K!&iS=P3)h}_`svRpvuAj48Y$(9)Ow^!6Zj^b5^WK>$LXD+@ne)NREpz8^+Q2< z69R*_NX}q@*DaR#S%v*Npm_Z zG5Y%#kYqa)|;pkIMpt|H^oXcxvfFM;l(Vjd)Hs} zE!XW_k$2l^%{L%Z$i@O32|PN2VI|^WNH`VyXs?qLNvQ|D>1O!wV3NqJ=X~5@$EhbQZ~_f=0b`${Q(pVMT_I(iv)=V@Mu9M zN_H9|WVy>JcrgX90E-Dyx)rR_a8n?oaf-q!j4=QWilUH;^C54~w{S%dHjUGw+x@xj zv@c!S9(~C@H(vIyBx_FYJFmU$o_dCtm^A;E%55%Dz?8=%;I7%y=vKlI%F$?`K*1wM zFl^aMJ8*$Q&hNCy5zvrm?y_X3Dv=IxV8u{)Dn$_w$3Z℘sTa__#Z-`^eCo5Iuq9 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%0D3OZzMvSC6_np6Ii_)H>wu7~0d> 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 e21!IgR09CUk4h153juTh0000 +
+ + BookMyVenue + +
+
+
+ Find Space + +
+
+ Host a venue + +
+
+
+ +
+ + ) +} + +export default NavBar \ No newline at end of file diff --git a/FRONTEND/src/index.css b/FRONTEND/src/index.css new file mode 100644 index 000000000..b6922e3fc --- /dev/null +++ b/FRONTEND/src/index.css @@ -0,0 +1,6 @@ +@import url('https://fonts.googleapis.com/css2?family=Be+Vietnam+Pro:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); +@import "tailwindcss"; + +@theme{ + --font-sans: 'Be Vietnam Pro', sans-serif; +} \ No newline at end of file diff --git a/FRONTEND/src/main.jsx b/FRONTEND/src/main.jsx new file mode 100644 index 000000000..c8707f8b5 --- /dev/null +++ b/FRONTEND/src/main.jsx @@ -0,0 +1,14 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.jsx' + +import { BrowserRouter } from 'react-router-dom' + +createRoot(document.getElementById('root')).render( + + + + + , +) diff --git a/FRONTEND/src/pages/HomePage.jsx b/FRONTEND/src/pages/HomePage.jsx new file mode 100644 index 000000000..09feaa76e --- /dev/null +++ b/FRONTEND/src/pages/HomePage.jsx @@ -0,0 +1,14 @@ + + +function HomePage() { + return ( +
+
+

Find the Perfect Venue for Your Event.

+

Discover and book unique spaces for your next gathering, party, or meeting.

+
+
+ ) +} + +export default HomePage \ No newline at end of file diff --git a/FRONTEND/vite.config.js b/FRONTEND/vite.config.js new file mode 100644 index 000000000..7575138c1 --- /dev/null +++ b/FRONTEND/vite.config.js @@ -0,0 +1,8 @@ +import { defineConfig } from 'vite' +import tailwindcss from '@tailwindcss/vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], +}) From 292393c69ee7806cd508a09fad1adbb6b5f40132 Mon Sep 17 00:00:00 2001 From: nafanpe Date: Tue, 26 May 2026 21:08:12 +0530 Subject: [PATCH 02/95] feat: search bar and filters --- FRONTEND/src/components/Filters.jsx | 39 +++++++++++++++++++++++++++++ FRONTEND/src/pages/HomePage.jsx | 26 ++++++++++++++----- 2 files changed, 59 insertions(+), 6 deletions(-) create mode 100644 FRONTEND/src/components/Filters.jsx diff --git a/FRONTEND/src/components/Filters.jsx b/FRONTEND/src/components/Filters.jsx new file mode 100644 index 000000000..3ba59f905 --- /dev/null +++ b/FRONTEND/src/components/Filters.jsx @@ -0,0 +1,39 @@ +import { MapPin } from "@mynaui/icons-react"; +import { Kanban } from "@mynaui/icons-react"; +import { Dollar } from "@mynaui/icons-react"; +import { Speaker } from "@mynaui/icons-react"; +import { ChevronDown } from "@mynaui/icons-react"; +import { Config } from "@mynaui/icons-react"; + +function Filters() { + + const dropdownFilters = [ + { name: "Location", icon: }, + { name: "Venue Type", icon: }, + { name: "Budget", icon: }, + { name: "Amenities", icon: } + ] + + return ( +
+ {dropdownFilters.map((filter, index) => ( + + ))} + { + + } +
+ ) +} + +export default Filters \ No newline at end of file diff --git a/FRONTEND/src/pages/HomePage.jsx b/FRONTEND/src/pages/HomePage.jsx index 09feaa76e..0d5801b46 100644 --- a/FRONTEND/src/pages/HomePage.jsx +++ b/FRONTEND/src/pages/HomePage.jsx @@ -1,12 +1,26 @@ - +import Filters from "../components/Filters" function HomePage() { + + const HeroContent = ( +
+

Find the Perfect Venue for Your Event.

+

Discover and book unique spaces for your next gathering, party, or meeting.

+
+ ) + + const Searchbar = ( +
+ + +
+ ) + return ( -
-
-

Find the Perfect Venue for Your Event.

-

Discover and book unique spaces for your next gathering, party, or meeting.

-
+
+ {HeroContent} + {Searchbar} +
) } From 52c597a33a4548d444d471ad2e7ec9c5cfb2f453 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Wed, 27 May 2026 21:25:09 +0530 Subject: [PATCH 03/95] backend initilized --- BACKEND/app/main.py | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 BACKEND/app/main.py diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py new file mode 100644 index 000000000..9b48881ec --- /dev/null +++ b/BACKEND/app/main.py @@ -0,0 +1,11 @@ +from fastapi import FastAPI + +app = FastAPI() + +@app.get("/") +def read_root(): + return {"Hello": "World"} + +@app.get("/items/{item_id}") +def read_item(item_id: int, q: str = None): + return {"item_id": item_id, "q": q} From cdcdd113eb910d804e49250f9881bf5fd8ada117 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Wed, 27 May 2026 21:30:48 +0530 Subject: [PATCH 04/95] git ignore updated --- .gitignore | 79 +++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 35fa4f81d..3d3aa02c6 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,6 @@ +# ========================= # Logs +# ========================= logs *.log npm-debug.log* @@ -7,28 +9,79 @@ yarn-error.log* pnpm-debug.log* lerna-debug.log* -node_modules -dist -dist-ssr +# ========================= +# Node / React +# ========================= +node_modules/ +dist/ +dist-ssr/ +coverage/ +.cache/ + +# ========================= +# Python +# ========================= +__pycache__/ +*.py[cod] +*$py.class + +# Virtual environments +venv/ +.venv/ +env/ + +# Python build files +build/ +develop-eggs/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +# ========================= +# Environment files +# ========================= +.env +.env.local +.env.development.local +.env.test.local +.env.production.local *.local -# Editor directories and files +# ========================= +# Editor / IDE +# ========================= .vscode/* !.vscode/extensions.json -.idea + +.idea/ .DS_Store + +# Visual Studio *.suo *.ntvs* *.njsproj *.sln + +# Vim *.sw? -#env files -.env -.env.local -.env.development.local -.env.test.local -.env.production.local +# ========================= +# Lint / Cache +# ========================= +.eslintcache +.pytest_cache/ +.mypy_cache/ -#linting and caching -.eslintcache \ No newline at end of file +# ========================= +# OS generated +# ========================= +Thumbs.db \ No newline at end of file From 716a9dc34c5c156ba07e88d9ef5a398842d6214e Mon Sep 17 00:00:00 2001 From: nafanpe Date: Wed, 27 May 2026 23:54:52 +0530 Subject: [PATCH 05/95] feat:Add Venue Grid and Responsiveness --- FRONTEND/src/components/Filters.jsx | 21 +++---- FRONTEND/src/components/Navbar.jsx | 88 +++++++++++++++++++++------ FRONTEND/src/components/VenueGrid.jsx | 69 +++++++++++++++++++++ FRONTEND/src/data/VenueCardData.jsx | 45 ++++++++++++++ FRONTEND/src/pages/HomePage.jsx | 61 ++++++++++++------- 5 files changed, 234 insertions(+), 50 deletions(-) create mode 100644 FRONTEND/src/components/VenueGrid.jsx create mode 100644 FRONTEND/src/data/VenueCardData.jsx diff --git a/FRONTEND/src/components/Filters.jsx b/FRONTEND/src/components/Filters.jsx index 3ba59f905..4872eaedb 100644 --- a/FRONTEND/src/components/Filters.jsx +++ b/FRONTEND/src/components/Filters.jsx @@ -15,23 +15,22 @@ function Filters() { ] return ( -
+
{dropdownFilters.map((filter, index) => ( ))} - { - - } + +
) } diff --git a/FRONTEND/src/components/Navbar.jsx b/FRONTEND/src/components/Navbar.jsx index 94bf77cf8..2cbf001c4 100644 --- a/FRONTEND/src/components/Navbar.jsx +++ b/FRONTEND/src/components/Navbar.jsx @@ -1,29 +1,83 @@ import { Link } from "react-router-dom" +import { useState } from "react" import logo from "../assets/Logo.png" function NavBar() { - return ( -
-
- - BookMyVenue - -
-
-
- Find Space - + + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + + + const toggleMenu = () => { + setIsMobileMenuOpen(!isMobileMenuOpen); + }; + + return ( +
+ {/* Main Navbar */} +
+ + {/* Logo */} +
+ + BookMyVenue +
-
- Host a venue - + + {/* Desktop Nav Links (Hidden on Mobile) */} +
+
+ Find Space + +
+
+ Host a venue + +
+
+ + {/* Desktop User Actions (Hidden on Mobile) */} +
+ +
+ + {/* Mobile Hamburger Toggle (Visible only on Mobile) */} +
+
-
- + + {/* Mobile Dropdown Menu */} +
+ + Find Space + + + Host a venue + +
- ) + ) } export default NavBar \ No newline at end of file diff --git a/FRONTEND/src/components/VenueGrid.jsx b/FRONTEND/src/components/VenueGrid.jsx new file mode 100644 index 000000000..a416b2e6f --- /dev/null +++ b/FRONTEND/src/components/VenueGrid.jsx @@ -0,0 +1,69 @@ +import { MapPin } from "@mynaui/icons-react"; +import { VENUE_DATA } from "../data/VenueCardData"; + +function GridHeader({ venueCount }) { + return ( +
+
+

+ Venues for you +

+
+ {venueCount} venues found +
+
+
+
+ ); +} + +function VenueCard({ venue }) { + const gridSpan = venue.isLarge ? "md:col-span-2 md:row-span-2" : "col-span-1"; + const titleSize = venue.isLarge ? "text-xl" : "text-sm"; + const detailSize = venue.isLarge ? "text-md" : "text-sm"; + const priceSize = venue.isLarge ? "text-lg" : "text-sm"; + const imageSize = venue.isLarge ? "h-144" : "h-48"; + + return ( +
+ {venue.title} + +
+

+ {venue.title} +

+ +
+ +

+ {venue.details} +

+
+ +

+ {venue.price} +

+
+
+ ); +} + +export default function VenueGrid() { + return ( +
+ + +
+ {VENUE_DATA.map((venue) => ( + + ))} +
+
+ ); +} \ No newline at end of file diff --git a/FRONTEND/src/data/VenueCardData.jsx b/FRONTEND/src/data/VenueCardData.jsx new file mode 100644 index 000000000..4b43d0eab --- /dev/null +++ b/FRONTEND/src/data/VenueCardData.jsx @@ -0,0 +1,45 @@ +export const VENUE_DATA = [ + { + id: 1, + title: "The Green Room at West Loop", + details: "Downtown District • 1.2 miles away", + price: "$120/hr", + rating: "4.9", + reviews: "125", + tag: "Popular Choice", + tagColor: "bg-cyan-50 text-cyan-700", + imageUrl: "https://images.unsplash.com/photo-1572025442646-866d16c84a54?auto=format&fit=crop&q=80&w=800", + isLarge: true, + }, + { + id: 2, + title: "Creative Hub Studio", + details: "Arts District • 0.8 miles away", + price: "$85/hr", + imageUrl: "https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&q=80&w=600", + }, + { + id: 3, + title: "The Rooftop Terrace", + details: "Lush greenery • Capacity: 50", + price: "$150/hr", + rating: "4.8", + imageUrl: "https://www.greenply.com:5001/originalfile1769165698904-875.jpg", + }, + { + id: 4, + title: "The Heritage Boardroom", + details: "Bazaar District", + price: "$60/hr", + rating: "4.7", + imageUrl: "https://images.unsplash.com/photo-1505691938895-1758d7feb511?auto=format&fit=crop&q=80&w=600", + }, + { + id: 5, + title: "Modern Innovation Hub", + details: "Manhan District", + price: "$95/hr", + rating: "4.9", + imageUrl: "https://images.unsplash.com/photo-1517502884422-41eaead166d4?auto=format&fit=crop&q=80&w=600", + } +]; \ No newline at end of file diff --git a/FRONTEND/src/pages/HomePage.jsx b/FRONTEND/src/pages/HomePage.jsx index 0d5801b46..a383e4686 100644 --- a/FRONTEND/src/pages/HomePage.jsx +++ b/FRONTEND/src/pages/HomePage.jsx @@ -1,28 +1,45 @@ -import Filters from "../components/Filters" +import Filters from "../components/Filters"; +import VenueGrid from "../components/VenueGrid"; -function HomePage() { - - const HeroContent = ( -
-

Find the Perfect Venue for Your Event.

-

Discover and book unique spaces for your next gathering, party, or meeting.

-
- ) - - const Searchbar = ( -
- - -
- ) +export default function HomePage() { + return ( + <> +
+ + + +
+ + + ); +} +function HeroContent() { return ( -
- {HeroContent} - {Searchbar} - +
+

+ Find the Perfect Venue for Your Event. +

+

+ Discover and book unique spaces for your next gathering, party, or meeting. +

- ) + ); } -export default HomePage \ No newline at end of file +function Searchbar() { + return ( +
+ + +
+ ); +} \ No newline at end of file From 640e9962aa3ba1c2b8275494cae69307e416a3f8 Mon Sep 17 00:00:00 2001 From: nafanpe Date: Mon, 1 Jun 2026 14:05:33 +0530 Subject: [PATCH 06/95] feat:Add Filter Dropdown --- FRONTEND/src/components/Filters.jsx | 89 ++++++++++++++++++++++++----- 1 file changed, 76 insertions(+), 13 deletions(-) diff --git a/FRONTEND/src/components/Filters.jsx b/FRONTEND/src/components/Filters.jsx index 4872eaedb..ceaacb6e9 100644 --- a/FRONTEND/src/components/Filters.jsx +++ b/FRONTEND/src/components/Filters.jsx @@ -4,27 +4,90 @@ import { Dollar } from "@mynaui/icons-react"; import { Speaker } from "@mynaui/icons-react"; import { ChevronDown } from "@mynaui/icons-react"; import { Config } from "@mynaui/icons-react"; +import { useEffect, useState, useRef } from "react"; function Filters() { + const [openDropdown, setOpenDropdown] = useState(null) + const containerRef = useRef(null) + + const dropdownFilters = [ - { name: "Location", icon: }, - { name: "Venue Type", icon: }, - { name: "Budget", icon: }, - { name: "Amenities", icon: } + { + name: "Location", + icon: , + options: ["Downtown", "Suburbs", "City Center", "Beachfront"] + }, + { + name: "Venue Type", + icon: , + options: ["Indoor", "Outdoor", "Rooftop", "Barn"] + }, + { + name: "Budget", + icon: , + options: ["$ (Under $500)", "$$ ($500 - $1k)", "$$$ ($1k - $5k)", "$$$$ ($5k+)"] + }, + { + name: "Amenities", + icon: , + options: ["WiFi", "Parking", "Catering", "AV Equipment"] + } ] + useEffect(() => { + const handleClickOutside = (event) => { + if (containerRef.current && !containerRef.current.contains(event.target)){ + setOpenDropdown(null) + }} + + document.addEventListener("mousedown", handleClickOutside) + return() => { + document.removeEventListener("mousedown", handleClickOutside) + } + }, []); + + const toggleDropdown = (name) => { + setOpenDropdown(openDropdown === name ? null : name); + } + return ( -
+
{dropdownFilters.map((filter, index) => ( - +
+ + +
+ {filter.options.map((option, i) => ( + + ))} +
+
))} + + {/* Venue Owner Button */} + +
+ + {/* Input Form */} +
e.preventDefault()}> +
+ + +
+ +
+ + +
+ +
+ + +
+ +
+ + +
+ + +
+ + {/* Footer Link */} +

+ Already have an account?{' '} + + Log In + +

+ +
+
+
+ ) +} + +export default LoginPage \ No newline at end of file From 6a071d9b2c0bc7435d4f175ffe79a7f58194c69d Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Thu, 4 Jun 2026 01:00:10 +0530 Subject: [PATCH 08/95] Add JWT authentication, signup/login APIs, and PostgreSQL connection --- BACKEND/app/api/auth_routes.py | 52 +++++++++++++++ BACKEND/app/api/user_routes.py | 16 +++++ BACKEND/app/core/config.py | 12 ++++ BACKEND/app/core/dependencies.py | 28 ++++++++ BACKEND/app/core/security.py | 21 ++++++ BACKEND/app/db/database.py | 10 +++ BACKEND/app/db/session.py | 19 ++++++ BACKEND/app/main.py | 20 ++++-- BACKEND/app/model/user.py | 10 +++ BACKEND/app/schema/auth.py | 10 +++ BACKEND/app/services/auth_service.py | 96 ++++++++++++++++++++++++++++ 11 files changed, 289 insertions(+), 5 deletions(-) create mode 100644 BACKEND/app/api/auth_routes.py create mode 100644 BACKEND/app/api/user_routes.py create mode 100644 BACKEND/app/core/config.py create mode 100644 BACKEND/app/core/dependencies.py create mode 100644 BACKEND/app/core/security.py create mode 100644 BACKEND/app/db/database.py create mode 100644 BACKEND/app/db/session.py create mode 100644 BACKEND/app/model/user.py create mode 100644 BACKEND/app/schema/auth.py create mode 100644 BACKEND/app/services/auth_service.py diff --git a/BACKEND/app/api/auth_routes.py b/BACKEND/app/api/auth_routes.py new file mode 100644 index 000000000..ef949cfcd --- /dev/null +++ b/BACKEND/app/api/auth_routes.py @@ -0,0 +1,52 @@ +from fastapi import APIRouter, Depends, HTTPException +from sqlalchemy.orm import Session + +from app.db.session import get_db +from app.schema.auth import ( + SignupRequest, + LoginRequest +) +from app.services.auth_service import ( + signup, + login +) + +router = APIRouter( + prefix="/auth", + tags=["Authentication"] +) + +@router.post("/signup") +def signup_user( + payload: SignupRequest, + db: Session = Depends(get_db) +): + try: + return signup( + db, + payload.name, + payload.email, + payload.password + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + +@router.post("/login") +def login_user( + payload: LoginRequest, + db: Session = Depends(get_db) +): + try: + return login( + db, + payload.email, + payload.password + ) + except Exception as e: + raise HTTPException( + status_code=401, + detail=str(e) + ) \ No newline at end of file diff --git a/BACKEND/app/api/user_routes.py b/BACKEND/app/api/user_routes.py new file mode 100644 index 000000000..39978deb5 --- /dev/null +++ b/BACKEND/app/api/user_routes.py @@ -0,0 +1,16 @@ +from fastapi import APIRouter, Depends +from app.core.dependencies import get_current_user + +router = APIRouter( + prefix="/user", + tags=["User"] +) + +@router.get("/profile") +def profile( + current_user=Depends(get_current_user) +): + return { + "message": "Authorized", + "user": current_user + } \ No newline at end of file diff --git a/BACKEND/app/core/config.py b/BACKEND/app/core/config.py new file mode 100644 index 000000000..dc23a5e54 --- /dev/null +++ b/BACKEND/app/core/config.py @@ -0,0 +1,12 @@ +from pydantic_settings import BaseSettings + +class Settings(BaseSettings): + DATABASE_URL: str + SECRET_KEY: str + ALGORITHM: str = "HS256" + ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 + + class Config: + env_file = ".env" + +settings = Settings() \ No newline at end of file diff --git a/BACKEND/app/core/dependencies.py b/BACKEND/app/core/dependencies.py new file mode 100644 index 000000000..56e4478d2 --- /dev/null +++ b/BACKEND/app/core/dependencies.py @@ -0,0 +1,28 @@ +from jose import jwt, JWTError +from fastapi import Depends, HTTPException +from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials + +from app.core.config import settings + +security = HTTPBearer() + +def get_current_user( + credentials: HTTPAuthorizationCredentials = Depends(security) +): + + token = credentials.credentials + + try: + payload = jwt.decode( + token, + settings.SECRET_KEY, + algorithms=[settings.ALGORITHM] + ) + + return payload + + except JWTError: + raise HTTPException( + status_code=401, + detail="Invalid token" + ) \ No newline at end of file diff --git a/BACKEND/app/core/security.py b/BACKEND/app/core/security.py new file mode 100644 index 000000000..edbf51687 --- /dev/null +++ b/BACKEND/app/core/security.py @@ -0,0 +1,21 @@ +from datetime import datetime, timedelta +from jose import jwt + +from app.core.config import settings + +def create_access_token(data: dict): + + payload = data.copy() + + payload["exp"] = ( + datetime.utcnow() + + timedelta( + minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES + ) + ) + + return jwt.encode( + payload, + settings.SECRET_KEY, + algorithm=settings.ALGORITHM + ) \ No newline at end of file diff --git a/BACKEND/app/db/database.py b/BACKEND/app/db/database.py new file mode 100644 index 000000000..917fab201 --- /dev/null +++ b/BACKEND/app/db/database.py @@ -0,0 +1,10 @@ +from sqlalchemy import create_engine +from sqlalchemy.orm import declarative_base +from app.core.config import settings + +engine = create_engine(settings.DATABASE_URL) + +with engine.connect() as conn: + print("Connected successfully!") + +Base = declarative_base() \ No newline at end of file diff --git a/BACKEND/app/db/session.py b/BACKEND/app/db/session.py new file mode 100644 index 000000000..d398892e2 --- /dev/null +++ b/BACKEND/app/db/session.py @@ -0,0 +1,19 @@ +from sqlalchemy.orm import sessionmaker + +from app.db.database import engine + +SessionLocal = sessionmaker( + autocommit=False, + autoflush=False, + bind=engine +) + + +def get_db(): + db = SessionLocal() + + try: + yield db + + finally: + db.close() \ No newline at end of file diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py index 9b48881ec..86d6ac04c 100644 --- a/BACKEND/app/main.py +++ b/BACKEND/app/main.py @@ -1,11 +1,21 @@ from fastapi import FastAPI +from app.db.database import Base, engine +from app.model.user import User + +from app.api.user_routes import router as user_router +from app.api.auth_routes import router as auth_router + +Base.metadata.create_all(bind=engine) + app = FastAPI() +app.include_router(user_router) +app.include_router(auth_router) + + @app.get("/") def read_root(): - return {"Hello": "World"} - -@app.get("/items/{item_id}") -def read_item(item_id: int, q: str = None): - return {"item_id": item_id, "q": q} + return { + "message": "API Running" + } diff --git a/BACKEND/app/model/user.py b/BACKEND/app/model/user.py new file mode 100644 index 000000000..e0846cf8d --- /dev/null +++ b/BACKEND/app/model/user.py @@ -0,0 +1,10 @@ +from sqlalchemy import Column, Integer, String +from app.db.database import Base + +class User(Base): + __tablename__ = "users" + + id = Column(Integer, primary_key=True, index=True) + name = Column(String(100)) + email = Column(String(255), unique=True, nullable=False) + password = Column(String(255), nullable=False) \ No newline at end of file diff --git a/BACKEND/app/schema/auth.py b/BACKEND/app/schema/auth.py new file mode 100644 index 000000000..5e5d15dce --- /dev/null +++ b/BACKEND/app/schema/auth.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel, EmailStr + +class SignupRequest(BaseModel): + name: str + email: EmailStr + password: str + +class LoginRequest(BaseModel): + email: EmailStr + password: str \ No newline at end of file diff --git a/BACKEND/app/services/auth_service.py b/BACKEND/app/services/auth_service.py new file mode 100644 index 000000000..f8a377028 --- /dev/null +++ b/BACKEND/app/services/auth_service.py @@ -0,0 +1,96 @@ +from sqlalchemy.orm import Session + +from passlib.context import CryptContext + +from app.model.user import User + +from app.core.security import ( + create_access_token +) + +pwd_context = CryptContext( + schemes=["bcrypt"], + deprecated="auto" +) + + +def signup( + db: Session, + name: str, + email: str, + password: str +): + + existing_user = ( + db.query(User) + .filter(User.email == email) + .first() + ) + + if existing_user: + raise Exception( + "Email already registered" + ) + + hashed_password = ( + pwd_context.hash(password) + ) + + user = User( + name=name, + email=email, + password=hashed_password + ) + + db.add(user) + + db.commit() + + db.refresh(user) + + return { + "message": "Signup successful" + } + + +def login( + db: Session, + email: str, + password: str +): + + user = ( + db.query(User) + .filter(User.email == email) + .first() + ) + + if not user: + raise Exception( + "Invalid credentials" + ) + + if not pwd_context.verify( + password, + user.password + ): + raise Exception( + "Invalid credentials" + ) + + token = create_access_token( + { + "sub": str(user.id), + "email": user.email + } + ) + + return { + "access_token": token, + "token_type": "bearer", + "user": { + "id": user.id, + "name": user.name, + "email": user.email + } + } \ No newline at end of file From e12ab3c334bd8447fc9bb889b058528efe1d5ccb Mon Sep 17 00:00:00 2001 From: nafanpe Date: Sun, 7 Jun 2026 22:19:43 +0530 Subject: [PATCH 09/95] refactor: Add Logic to Auth page --- FRONTEND/src/App.jsx | 2 +- FRONTEND/src/hooks/useAuthForm.jsx | 95 +++++++++++++++ FRONTEND/src/pages/LoginPage.jsx | 188 +++++++++++++++++------------ 3 files changed, 205 insertions(+), 80 deletions(-) create mode 100644 FRONTEND/src/hooks/useAuthForm.jsx diff --git a/FRONTEND/src/App.jsx b/FRONTEND/src/App.jsx index 9db67478f..1ef721b0d 100644 --- a/FRONTEND/src/App.jsx +++ b/FRONTEND/src/App.jsx @@ -9,7 +9,7 @@ function App() {
} /> - } /> + } />
diff --git a/FRONTEND/src/hooks/useAuthForm.jsx b/FRONTEND/src/hooks/useAuthForm.jsx new file mode 100644 index 000000000..4a4bcc9ad --- /dev/null +++ b/FRONTEND/src/hooks/useAuthForm.jsx @@ -0,0 +1,95 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; + +export const useAuthForm = () => { + const navigate = useNavigate(); + + const [role, setRole] = useState('organizer'); + const [isLoginView, setIsLoginView] = useState(false); + const [error, setError] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const [formData, setFormData] = useState({ + name: '', + email: '', + password: '', + }); + + const handleChange = (e) => { + setFormData({ + ...formData, + [e.target.name]: e.target.value, + }) + setError(''); + } + + const toggleView = (e) => { + e.preventDefault(); + setIsLoginView(!isLoginView); + setError(''); + setFormData({ name: '', email: '', password: ''}); + }; + + const handleSubmit = async (e) => { + e.preventDefault(); + setError('') + + // validation for both login and sign up forms + if(!formData.email || !formData.password){ + return setError('Please fill in all required fields.'); + } + + // validation for sign up form only + if(!isLoginView){ + if(!formData.name) return setError('Please fill Your Name'); + if(formData.password.length < 6) return setError('Password must be at least 6 characters long.'); + } + + setIsLoading(true); + + try{ + const BASE_URL = "https://unsaving-channing-sisterly.ngrok-free.dev" + + const endpoint = isLoginView ? "/auth/login" : "/auth/signup"; + + const payload = isLoginView + ? {email: formData.email, password: formData.password} + : {name: formData.name, email: formData.email, password: formData.password, role: role} + + const response = await fetch(BASE_URL + endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(payload) + }) + + const data = await response.json(); + + if(!response.ok){ + throw new Error(data.message || "Something went wrong. Please try again."); + } + + console.log("Success! Data sent to server:", { view: isLoginView ? 'Login' : 'Signup', role, ...formData }); + + if(data.access_token){ + localStorage.setItem('authToken', data.access_token); + } + + navigate('/') + alert(isLoginView ? 'Logged in successfully!' : 'Account created successfully!'); + + } catch (err) { + console.error("Error during form submission:", err); + setError(err.message) + + } finally { + setIsLoading(false); + } + } + return { + role, setRole, + isLoginView, toggleView, + formData, handleChange, + error, isLoading, handleSubmit + } +} \ No newline at end of file diff --git a/FRONTEND/src/pages/LoginPage.jsx b/FRONTEND/src/pages/LoginPage.jsx index c845c6321..c8ccc47d4 100644 --- a/FRONTEND/src/pages/LoginPage.jsx +++ b/FRONTEND/src/pages/LoginPage.jsx @@ -1,92 +1,121 @@ import Logo from "../assets/Logo.png" +import { useAuthForm } from "../hooks/useAuthForm"; + import { useState } from "react"; -import { Users, Store, ArrowRight } from "@mynaui/icons-react"; - +import { useNavigate } from "react-router-dom"; +import { Users, Store, ArrowRight, SpinnerOne } from "@mynaui/icons-react"; + +const LeftPanel = () => ( +
+ Login Illustration + +
+ Logo +

Spaces that Bring People Together

+

Join a Community of organizers and hosts
+ dedicated to creating memorable Experiences!

+
+
+) function LoginPage() { - const [role, setRole] = useState('organizer'); + const { + role, setRole, isLoginView, toggleView, + formData, handleChange, error, isLoading, handleSubmit + } = useAuthForm(); + return (
- {/* Left Panel */} -
- Login Illustration - -
- Logo -

Spaces that Bring People Together

-

Join a Community of organizers and hosts
- dedicated to creating memorable Experiences!

-
-
+ - {/* Right Panel */} -
-
+
+
{/* Form Header */}
-

Create an account

-

Let's get started by selecting your role.

+

{ + isLoginView ? "Log In to Your Account" : "Create Your Account" + }

+

{ + isLoginView ? "Welcome back! Please enter your details." : "Join us and start creating unforgettable experiences!" + }

{/* Role Selection Toggle */} -
- {/* Organizer Button */} - + {!isLoginView && ( +
+ {/* Organizer Button */} + - {/* Venue Owner Button */} - -
+ {/* Venue Owner Button */} + +
+ )} - {/* Input Form */} -
e.preventDefault()}> -
- - + {/* Form Error Message Display */} + {error && ( +
+ {error}
+ )} + + {/* Input Form */} + + {!isLoginView && ( +
+ + +
+ )}
@@ -94,34 +123,35 @@ function LoginPage() { -
- -
- -
{/* Footer Link */}

- Already have an account?{' '} - - Log In + {isLoginView ? "Don't have an account? " : "Already have an account? "} + + {isLoginView ? 'Sign Up' : "Log In"}

From c89cd695c7b8c1a3564f2cad8fdce991d781f6e2 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Wed, 10 Jun 2026 17:59:09 +0530 Subject: [PATCH 10/95] role added, venue schema added --- BACKEND/app/api/auth_routes.py | 3 ++- BACKEND/app/main.py | 22 ++++++++++++++-- BACKEND/app/model/user.py | 15 +++++++++-- BACKEND/app/model/venue.py | 39 ++++++++++++++++++++++++++++ BACKEND/app/schema/auth.py | 1 + BACKEND/app/services/auth_service.py | 12 ++++++--- 6 files changed, 83 insertions(+), 9 deletions(-) create mode 100644 BACKEND/app/model/venue.py diff --git a/BACKEND/app/api/auth_routes.py b/BACKEND/app/api/auth_routes.py index ef949cfcd..d59c3bf7f 100644 --- a/BACKEND/app/api/auth_routes.py +++ b/BACKEND/app/api/auth_routes.py @@ -26,7 +26,8 @@ def signup_user( db, payload.name, payload.email, - payload.password + payload.password, + payload.role ) except Exception as e: raise HTTPException( diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py index 86d6ac04c..0557bfda1 100644 --- a/BACKEND/app/main.py +++ b/BACKEND/app/main.py @@ -2,14 +2,32 @@ from app.db.database import Base, engine from app.model.user import User +from app.model.venue import Venue from app.api.user_routes import router as user_router from app.api.auth_routes import router as auth_router +from fastapi.middleware.cors import CORSMiddleware -Base.metadata.create_all(bind=engine) - + app = FastAPI() +@app.on_event("startup") +async def startup(): + Base.metadata.create_all(bind=engine) + +origins = [ + "http://localhost:5173", + "http://127.0.0.1:5173", +] + +app.add_middleware( + CORSMiddleware, + allow_origins=origins, + allow_credentials=True, + allow_methods=["*"], + allow_headers=["*"], +) + app.include_router(user_router) app.include_router(auth_router) diff --git a/BACKEND/app/model/user.py b/BACKEND/app/model/user.py index e0846cf8d..51f852da3 100644 --- a/BACKEND/app/model/user.py +++ b/BACKEND/app/model/user.py @@ -1,4 +1,5 @@ -from sqlalchemy import Column, Integer, String +from sqlalchemy import Column, Integer, String, DateTime, func +from sqlalchemy.orm import relationship from app.db.database import Base class User(Base): @@ -7,4 +8,14 @@ class User(Base): id = Column(Integer, primary_key=True, index=True) name = Column(String(100)) email = Column(String(255), unique=True, nullable=False) - password = Column(String(255), nullable=False) \ No newline at end of file + password = Column(String(255), nullable=False) + role = Column(String(50), nullable=False, default="user") + + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now() + ) + + venues = relationship("Venue", back_populates="user") \ No newline at end of file diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py new file mode 100644 index 000000000..0c4f8a1d6 --- /dev/null +++ b/BACKEND/app/model/venue.py @@ -0,0 +1,39 @@ +from sqlalchemy import ( + Column, + Integer, + String, + Text, + DateTime, + ForeignKey, + func +) +from sqlalchemy.orm import relationship +from app.db.database import Base + +class Venue(Base): + __tablename__ = "venues" + + id = Column(Integer, primary_key=True, index=True) + user_id = Column( + Integer, + ForeignKey("users.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + venue_name = Column(String(100), nullable=False) + venue_description = Column(Text) + location = Column(String(255), nullable=False) + capacity = Column(Integer, nullable=False) + venue_price = Column(Integer, nullable=False) + + + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now() + ) + + user = relationship("User", back_populates="venues") + diff --git a/BACKEND/app/schema/auth.py b/BACKEND/app/schema/auth.py index 5e5d15dce..ef16ef791 100644 --- a/BACKEND/app/schema/auth.py +++ b/BACKEND/app/schema/auth.py @@ -4,6 +4,7 @@ class SignupRequest(BaseModel): name: str email: EmailStr password: str + role: str class LoginRequest(BaseModel): email: EmailStr diff --git a/BACKEND/app/services/auth_service.py b/BACKEND/app/services/auth_service.py index f8a377028..dfeb78760 100644 --- a/BACKEND/app/services/auth_service.py +++ b/BACKEND/app/services/auth_service.py @@ -18,7 +18,8 @@ def signup( db: Session, name: str, email: str, - password: str + password: str, + role: str ): existing_user = ( @@ -39,7 +40,8 @@ def signup( user = User( name=name, email=email, - password=hashed_password + password=hashed_password, + role=role ) db.add(user) @@ -81,7 +83,8 @@ def login( token = create_access_token( { "sub": str(user.id), - "email": user.email + "email": user.email, + "role": user.role } ) @@ -91,6 +94,7 @@ def login( "user": { "id": user.id, "name": user.name, - "email": user.email + "email": user.email, + "role": user.role } } \ No newline at end of file From 95c45a01a289eba9bcb41b336088bdf96fdfd49b Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Thu, 11 Jun 2026 23:31:13 +0530 Subject: [PATCH 11/95] venue images and amenities schema added --- BACKEND/app/model/user.py | 2 +- BACKEND/app/model/venue.py | 5 +++- BACKEND/app/model/venue_amenites.py | 39 +++++++++++++++++++++++++++++ BACKEND/app/model/venue_images.py | 37 +++++++++++++++++++++++++++ 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 BACKEND/app/model/venue_amenites.py create mode 100644 BACKEND/app/model/venue_images.py diff --git a/BACKEND/app/model/user.py b/BACKEND/app/model/user.py index 51f852da3..8ed17193a 100644 --- a/BACKEND/app/model/user.py +++ b/BACKEND/app/model/user.py @@ -18,4 +18,4 @@ class User(Base): onupdate=func.now() ) - venues = relationship("Venue", back_populates="user") \ No newline at end of file + venues = relationship("Venue", back_populates="user", cascade="all, delete-orphan") \ No newline at end of file diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py index 0c4f8a1d6..0347732a0 100644 --- a/BACKEND/app/model/venue.py +++ b/BACKEND/app/model/venue.py @@ -26,7 +26,8 @@ class Venue(Base): location = Column(String(255), nullable=False) capacity = Column(Integer, nullable=False) venue_price = Column(Integer, nullable=False) - + venue_availabilty = Column(String(255), nullable=False) # hourly or daily + is_available = Column(String(255), nullable=False) # turn on, off venue created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column( @@ -36,4 +37,6 @@ class Venue(Base): ) user = relationship("User", back_populates="venues") + venue_amenities = relationship("VenueAmenities", back_populates="venues", cascade="all, delete-orphan") + venue_images = relationship("VenueImages", back_populates="venues", cascade="all, delete-orphan") diff --git a/BACKEND/app/model/venue_amenites.py b/BACKEND/app/model/venue_amenites.py new file mode 100644 index 000000000..b67882fd9 --- /dev/null +++ b/BACKEND/app/model/venue_amenites.py @@ -0,0 +1,39 @@ +from sqlalchemy import ( + Column, + Integer, + String, + Boolean, + Text, + DateTime, + ForeignKey, + func +) +from sqlalchemy.orm import relationship +from app.db.database import Base + +class VenueAmenities(Base): + __tablename__ = "venue_amenities" + + id = Column(Integer, primary_key=True, index=True) + venue_id = Column( + Integer, + ForeignKey("venues.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + wifi = Column(Boolean, default=False) + kitchen = Column(Boolean, default=False) + parking = Column(Boolean, default=False) + ac = Column(Boolean, default=False) + + + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now() + ) + + venue = relationship("Venue", back_populates="venue_amenities") + diff --git a/BACKEND/app/model/venue_images.py b/BACKEND/app/model/venue_images.py new file mode 100644 index 000000000..c086da9c2 --- /dev/null +++ b/BACKEND/app/model/venue_images.py @@ -0,0 +1,37 @@ +from sqlalchemy import ( + Column, + Integer, + String, + Boolean, + Text, + DateTime, + ForeignKey, + func +) +from sqlalchemy.orm import relationship +from app.db.database import Base + +class VenueImages(Base): + __tablename__ = "venue_images" + + id = Column(Integer, primary_key=True, index=True) + venue_id = Column( + Integer, + ForeignKey("venues.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + image_url = Column(String(1000), nullable=False) + cover_image = Column(Boolean, default=False) + display_order = Column(Integer, default=1) + + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now() + ) + + venue = relationship("Venue", back_populates="venue_images") + From a8f16282a2ed1a76e45ff321de307955d0c1f949 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Fri, 12 Jun 2026 23:41:32 +0530 Subject: [PATCH 12/95] venue APIs --- BACKEND/alembic.ini | 149 ++++++++++++++++++ BACKEND/alembic/README | 1 + BACKEND/alembic/env.py | 90 +++++++++++ BACKEND/alembic/script.py.mako | 28 ++++ .../versions/136fe88b327a_add_venue_fields.py | 36 +++++ BACKEND/app/api/auth_routes.py | 5 +- BACKEND/app/api/venue_routes.py | 81 ++++++++++ BACKEND/app/main.py | 5 + BACKEND/app/model/venue.py | 7 +- BACKEND/app/schema/venue.py | 12 ++ BACKEND/app/services/venue_service.py | 82 ++++++++++ 11 files changed, 489 insertions(+), 7 deletions(-) create mode 100644 BACKEND/alembic.ini create mode 100644 BACKEND/alembic/README create mode 100644 BACKEND/alembic/env.py create mode 100644 BACKEND/alembic/script.py.mako create mode 100644 BACKEND/alembic/versions/136fe88b327a_add_venue_fields.py create mode 100644 BACKEND/app/api/venue_routes.py create mode 100644 BACKEND/app/schema/venue.py create mode 100644 BACKEND/app/services/venue_service.py diff --git a/BACKEND/alembic.ini b/BACKEND/alembic.ini new file mode 100644 index 000000000..807ded2d5 --- /dev/null +++ b/BACKEND/alembic.ini @@ -0,0 +1,149 @@ +# A generic, single database configuration. + +[alembic] +# path to migration scripts. +# this is typically a path given in POSIX (e.g. forward slashes) +# format, relative to the token %(here)s which refers to the location of this +# ini file +script_location = %(here)s/alembic + +# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s +# Uncomment the line below if you want the files to be prepended with date and time +# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file +# for all available tokens +# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s +# Or organize into date-based subdirectories (requires recursive_version_locations = true) +# file_template = %%(year)d/%%(month).2d/%%(day).2d_%%(hour).2d%%(minute).2d_%%(second).2d_%%(rev)s_%%(slug)s + +# sys.path path, will be prepended to sys.path if present. +# defaults to the current working directory. for multiple paths, the path separator +# is defined by "path_separator" below. +prepend_sys_path = . + + +# timezone to use when rendering the date within the migration file +# as well as the filename. +# If specified, requires the tzdata library which can be installed by adding +# `alembic[tz]` to the pip requirements. +# string value is passed to ZoneInfo() +# leave blank for localtime +# timezone = + +# max length of characters to apply to the "slug" field +# truncate_slug_length = 40 + +# set to 'true' to run the environment during +# the 'revision' command, regardless of autogenerate +# revision_environment = false + +# set to 'true' to allow .pyc and .pyo files without +# a source .py file to be detected as revisions in the +# versions/ directory +# sourceless = false + +# version location specification; This defaults +# to /versions. When using multiple version +# directories, initial revisions must be specified with --version-path. +# The path separator used here should be the separator specified by "path_separator" +# below. +# version_locations = %(here)s/bar:%(here)s/bat:%(here)s/alembic/versions + +# path_separator; This indicates what character is used to split lists of file +# paths, including version_locations and prepend_sys_path within configparser +# files such as alembic.ini. +# The default rendered in new alembic.ini files is "os", which uses os.pathsep +# to provide os-dependent path splitting. +# +# Note that in order to support legacy alembic.ini files, this default does NOT +# take place if path_separator is not present in alembic.ini. If this +# option is omitted entirely, fallback logic is as follows: +# +# 1. Parsing of the version_locations option falls back to using the legacy +# "version_path_separator" key, which if absent then falls back to the legacy +# behavior of splitting on spaces and/or commas. +# 2. Parsing of the prepend_sys_path option falls back to the legacy +# behavior of splitting on spaces, commas, or colons. +# +# Valid values for path_separator are: +# +# path_separator = : +# path_separator = ; +# path_separator = space +# path_separator = newline +# +# Use os.pathsep. Default configuration used for new projects. +path_separator = os + +# set to 'true' to search source files recursively +# in each "version_locations" directory +# new in Alembic version 1.10 +# recursive_version_locations = false + +# the output encoding used when revision files +# are written from script.py.mako +# output_encoding = utf-8 + +# database URL. This is consumed by the user-maintained env.py script only. +# other means of configuring database URLs may be customized within the env.py +# file. +sqlalchemy.url = driver://user:pass@localhost/dbname + + +[post_write_hooks] +# post_write_hooks defines scripts or Python functions that are run +# on newly generated revision scripts. See the documentation for further +# detail and examples + +# format using "black" - use the console_scripts runner, against the "black" entrypoint +# hooks = black +# black.type = console_scripts +# black.entrypoint = black +# black.options = -l 79 REVISION_SCRIPT_FILENAME + +# lint with attempts to fix using "ruff" - use the module runner, against the "ruff" module +# hooks = ruff +# ruff.type = module +# ruff.module = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Alternatively, use the exec runner to execute a binary found on your PATH +# hooks = ruff +# ruff.type = exec +# ruff.executable = ruff +# ruff.options = check --fix REVISION_SCRIPT_FILENAME + +# Logging configuration. This is also consumed by the user-maintained +# env.py script only. +[loggers] +keys = root,sqlalchemy,alembic + +[handlers] +keys = console + +[formatters] +keys = generic + +[logger_root] +level = WARNING +handlers = console +qualname = + +[logger_sqlalchemy] +level = WARNING +handlers = +qualname = sqlalchemy.engine + +[logger_alembic] +level = INFO +handlers = +qualname = alembic + +[handler_console] +class = StreamHandler +args = (sys.stderr,) +level = NOTSET +formatter = generic + +[formatter_generic] +format = %(levelname)-5.5s [%(name)s] %(message)s +datefmt = %H:%M:%S diff --git a/BACKEND/alembic/README b/BACKEND/alembic/README new file mode 100644 index 000000000..98e4f9c44 --- /dev/null +++ b/BACKEND/alembic/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/BACKEND/alembic/env.py b/BACKEND/alembic/env.py new file mode 100644 index 000000000..f49484544 --- /dev/null +++ b/BACKEND/alembic/env.py @@ -0,0 +1,90 @@ +from logging.config import fileConfig + +from sqlalchemy import engine_from_config +from sqlalchemy import pool + +from alembic import context +from app.db.database import Base + +# this is the Alembic Config object, which provides +# access to the values within the .ini file in use. +config = context.config +from app.core.config import settings + +config.set_main_option( + "sqlalchemy.url", + settings.DATABASE_URL +) + +# Interpret the config file for Python logging. +# This line sets up loggers basically. +if config.config_file_name is not None: + fileConfig(config.config_file_name) + +# add your model's MetaData object here +# for 'autogenerate' support +# from myapp import mymodel +from app.model.user import User +from app.model.venue import Venue +from app.model.venue_amenites import VenueAmenities +from app.model.venue_images import VenueImages + +# target_metadata = mymodel.Base.metadata +target_metadata = Base.metadata + +# other values from the config, defined by the needs of env.py, +# can be acquired: +# my_important_option = config.get_main_option("my_important_option") +# ... etc. + + +def run_migrations_offline() -> None: + """Run migrations in 'offline' mode. + + This configures the context with just a URL + and not an Engine, though an Engine is acceptable + here as well. By skipping the Engine creation + we don't even need a DBAPI to be available. + + Calls to context.execute() here emit the given string to the + script output. + + """ + url = config.get_main_option("sqlalchemy.url") + context.configure( + url=url, + target_metadata=target_metadata, + literal_binds=True, + dialect_opts={"paramstyle": "named"}, + ) + + with context.begin_transaction(): + context.run_migrations() + + +def run_migrations_online() -> None: + """Run migrations in 'online' mode. + + In this scenario we need to create an Engine + and associate a connection with the context. + + """ + connectable = engine_from_config( + config.get_section(config.config_ini_section, {}), + prefix="sqlalchemy.", + poolclass=pool.NullPool, + ) + + with connectable.connect() as connection: + context.configure( + connection=connection, target_metadata=target_metadata + ) + + with context.begin_transaction(): + context.run_migrations() + + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/BACKEND/alembic/script.py.mako b/BACKEND/alembic/script.py.mako new file mode 100644 index 000000000..11016301e --- /dev/null +++ b/BACKEND/alembic/script.py.mako @@ -0,0 +1,28 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision: str = ${repr(up_revision)} +down_revision: Union[str, Sequence[str], None] = ${repr(down_revision)} +branch_labels: Union[str, Sequence[str], None] = ${repr(branch_labels)} +depends_on: Union[str, Sequence[str], None] = ${repr(depends_on)} + + +def upgrade() -> None: + """Upgrade schema.""" + ${upgrades if upgrades else "pass"} + + +def downgrade() -> None: + """Downgrade schema.""" + ${downgrades if downgrades else "pass"} diff --git a/BACKEND/alembic/versions/136fe88b327a_add_venue_fields.py b/BACKEND/alembic/versions/136fe88b327a_add_venue_fields.py new file mode 100644 index 000000000..ac7bbd13d --- /dev/null +++ b/BACKEND/alembic/versions/136fe88b327a_add_venue_fields.py @@ -0,0 +1,36 @@ +"""add venue fields + +Revision ID: 136fe88b327a +Revises: +Create Date: 2026-06-12 14:27:25.878941 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '136fe88b327a' +down_revision: Union[str, Sequence[str], None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('venues', sa.Column('venue_price', sa.Integer(), nullable=False)) + op.add_column('venues', sa.Column('venue_availabilty', sa.String(length=255), nullable=False)) + op.add_column('venues', sa.Column('is_available', sa.String(length=255), nullable=False)) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('venues', 'is_available') + op.drop_column('venues', 'venue_availabilty') + op.drop_column('venues', 'venue_price') + # ### end Alembic commands ### diff --git a/BACKEND/app/api/auth_routes.py b/BACKEND/app/api/auth_routes.py index d59c3bf7f..1ec1e7c25 100644 --- a/BACKEND/app/api/auth_routes.py +++ b/BACKEND/app/api/auth_routes.py @@ -6,10 +6,7 @@ SignupRequest, LoginRequest ) -from app.services.auth_service import ( - signup, - login -) +from app.services.auth_service import ( signup, login ) router = APIRouter( prefix="/auth", diff --git a/BACKEND/app/api/venue_routes.py b/BACKEND/app/api/venue_routes.py new file mode 100644 index 000000000..769699881 --- /dev/null +++ b/BACKEND/app/api/venue_routes.py @@ -0,0 +1,81 @@ +from app.core.dependencies import get_current_user +from typing import Optional +from app.db.session import get_db + +from fastapi import APIRouter, Depends, Query, HTTPException +from sqlalchemy.orm import Session +from app.schema.venue import ( + BasicRequest +) +from app.services.venue_service import ( + get_venues, + get_venue_details_by_id, + add_venue, +) + +router = APIRouter( + prefix="/venues", + tags=["Venues"] +) + +@router.get("/") +def venues( + db: Session = Depends(get_db), + page_no: int = Query(1, ge=1, description="Page number"), + limit: int = Query(20, ge=1, le=100, description="Records per page"), + action: Optional[str] = Query(None, description="Filter by action"), + location: Optional[str] = Query(None, description="Filter by location"), + avaialability: Optional[str] = Query(None, description="Filter by location"), +): + try: + return get_venues( + db, + page_no, + limit, + action, + location, + avaialability + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + +@router.get("/details/{venue_id}") +def venues( + venue_id: int, + db: Session = Depends(get_db), +): + try: + return get_venue_details_by_id( + db, + venue_id + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + +@router.post("/basic-details") +def venues( + payload: BasicRequest, + db: Session = Depends(get_db), +): + try: + return add_venue( + db, + user_id=payload.user_id, + venue_name=payload.venue_name, + venue_description=payload.venue_description, + location=payload.location, + capacity=payload.capacity, + venue_price=payload.venue_price, + venue_availabilty=payload.venue_availabilty, + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) \ No newline at end of file diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py index 0557bfda1..d55ab1f97 100644 --- a/BACKEND/app/main.py +++ b/BACKEND/app/main.py @@ -3,9 +3,12 @@ from app.db.database import Base, engine from app.model.user import User from app.model.venue import Venue +from app.model.venue_amenites import VenueAmenities +from app.model.venue_images import VenueImages from app.api.user_routes import router as user_router from app.api.auth_routes import router as auth_router +from app.api.venue_routes import router as venue_router from fastapi.middleware.cors import CORSMiddleware @@ -14,6 +17,7 @@ @app.on_event("startup") async def startup(): Base.metadata.create_all(bind=engine) + print(Base.metadata.tables.keys()) origins = [ "http://localhost:5173", @@ -30,6 +34,7 @@ async def startup(): app.include_router(user_router) app.include_router(auth_router) +app.include_router(venue_router) @app.get("/") diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py index 0347732a0..d4e4061d0 100644 --- a/BACKEND/app/model/venue.py +++ b/BACKEND/app/model/venue.py @@ -27,7 +27,8 @@ class Venue(Base): capacity = Column(Integer, nullable=False) venue_price = Column(Integer, nullable=False) venue_availabilty = Column(String(255), nullable=False) # hourly or daily - is_available = Column(String(255), nullable=False) # turn on, off venue + is_available = Column(Boolean, nullable=False, default=False) + is_approved = Column(Boolean, nullable=False, default=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column( @@ -37,6 +38,6 @@ class Venue(Base): ) user = relationship("User", back_populates="venues") - venue_amenities = relationship("VenueAmenities", back_populates="venues", cascade="all, delete-orphan") - venue_images = relationship("VenueImages", back_populates="venues", cascade="all, delete-orphan") + venue_amenities = relationship("VenueAmenities", back_populates="venue", cascade="all, delete-orphan") + venue_images = relationship("VenueImages", back_populates="venue", cascade="all, delete-orphan") diff --git a/BACKEND/app/schema/venue.py b/BACKEND/app/schema/venue.py new file mode 100644 index 000000000..8151c559c --- /dev/null +++ b/BACKEND/app/schema/venue.py @@ -0,0 +1,12 @@ +from pydantic import BaseModel, Field +from typing import Literal + +class BasicRequest(BaseModel): + user_id: int = Field(..., gt=0) + venue_name: str = Field(..., min_length=1) + venue_description: str = Field(..., min_length=1) + location: str = Field(..., min_length=1) + capacity: int = Field(..., gt=0) + venue_price: int = Field(..., ge=0) + venue_availabilty: Literal["hourly", "daily"] + diff --git a/BACKEND/app/services/venue_service.py b/BACKEND/app/services/venue_service.py new file mode 100644 index 000000000..660c15afb --- /dev/null +++ b/BACKEND/app/services/venue_service.py @@ -0,0 +1,82 @@ +from sqlalchemy.orm import Session + +from app.model.venue import Venue +from app.model.user import User + +def get_venues( + db: Session, + page_no : int, + limit : int, + action : str, + location : str, + avaialability : str +): + offset = (page_no - 1) * limit + + venues = db.query(Venue).offset(offset).limit(limit).all() + + if not venues: + return { + "message": "venues are not added" + } + + return venues + +def get_venue_details_by_id( + db: Session, + venue_id : int, +): + + venue = ( + db.query(Venue) + .filter(Venue.id == venue_id) + .first() + ) + + if not venue: + return { + "message": "venues details is not present" + } + + return venue + + +def add_venue( + db: Session, + user_id: int, + venue_name: str, + venue_description: str, + location: str, + capacity: int, + venue_price: int, + venue_availabilty: str, +): + + user = ( + db.query(User) + .filter(User.id == user_id) + .first() + ) + + if not user: + raise Exception("Invalid user") + + new_venue = Venue( + user_id=user_id, + venue_name=venue_name, + venue_description=venue_description, + location=location, + capacity=capacity, + venue_price=venue_price, + venue_availabilty=venue_availabilty, + ) + + + db.add(new_venue) + db.commit() + db.refresh(new_venue) + + return new_venue + + + From 480cb1972e8712a03d66c18f9486410907fdbcd8 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Sat, 13 Jun 2026 21:09:40 +0530 Subject: [PATCH 13/95] venue amenities add api --- BACKEND/app/api/venue_routes.py | 29 +++++++++++++++++++-- BACKEND/app/model/venue.py | 3 ++- BACKEND/app/model/venue_amenites.py | 2 ++ BACKEND/app/schema/venue.py | 9 ++++++- BACKEND/app/services/venue_service.py | 37 ++++++++++++++++++++++++++- 5 files changed, 75 insertions(+), 5 deletions(-) diff --git a/BACKEND/app/api/venue_routes.py b/BACKEND/app/api/venue_routes.py index 769699881..bcd41aff9 100644 --- a/BACKEND/app/api/venue_routes.py +++ b/BACKEND/app/api/venue_routes.py @@ -5,12 +5,14 @@ from fastapi import APIRouter, Depends, Query, HTTPException from sqlalchemy.orm import Session from app.schema.venue import ( - BasicRequest + VenueDetailsCreate, + VenueAmenitiesCreate ) from app.services.venue_service import ( get_venues, get_venue_details_by_id, add_venue, + add_venue_amenities ) router = APIRouter( @@ -60,7 +62,7 @@ def venues( @router.post("/basic-details") def venues( - payload: BasicRequest, + payload: VenueDetailsCreate, db: Session = Depends(get_db), ): try: @@ -74,6 +76,29 @@ def venues( venue_price=payload.venue_price, venue_availabilty=payload.venue_availabilty, ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + +@router.post("/{venue_id}/amenities") +def venues( + payload: VenueAmenitiesCreate, + venue_id: int, + db: Session = Depends(get_db), +): + try: + return add_venue_amenities( + db=db, + venue_id=payload.user_id, + wifi=payload.wifi, + kitchen=payload.kitchen, + parking=payload.parking, + ac=payload.ac, + wheel_chair=payload.wheel_chair, + av_equipements=payload.av_equipements, + ) except Exception as e: raise HTTPException( status_code=400, diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py index d4e4061d0..8c5689423 100644 --- a/BACKEND/app/model/venue.py +++ b/BACKEND/app/model/venue.py @@ -5,7 +5,8 @@ Text, DateTime, ForeignKey, - func + func, + Boolean, ) from sqlalchemy.orm import relationship from app.db.database import Base diff --git a/BACKEND/app/model/venue_amenites.py b/BACKEND/app/model/venue_amenites.py index b67882fd9..76f844e94 100644 --- a/BACKEND/app/model/venue_amenites.py +++ b/BACKEND/app/model/venue_amenites.py @@ -26,6 +26,8 @@ class VenueAmenities(Base): kitchen = Column(Boolean, default=False) parking = Column(Boolean, default=False) ac = Column(Boolean, default=False) + wheel_chair = Column(Boolean, default=False) + av_equipements = Column(Boolean, default=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) diff --git a/BACKEND/app/schema/venue.py b/BACKEND/app/schema/venue.py index 8151c559c..0833249d5 100644 --- a/BACKEND/app/schema/venue.py +++ b/BACKEND/app/schema/venue.py @@ -1,7 +1,7 @@ from pydantic import BaseModel, Field from typing import Literal -class BasicRequest(BaseModel): +class VenueDetailsCreate(BaseModel): user_id: int = Field(..., gt=0) venue_name: str = Field(..., min_length=1) venue_description: str = Field(..., min_length=1) @@ -10,3 +10,10 @@ class BasicRequest(BaseModel): venue_price: int = Field(..., ge=0) venue_availabilty: Literal["hourly", "daily"] +class VenueAmenitiesCreate(BaseModel): + wifi: bool = False + kitchen: bool = False + parking: bool = False + ac: bool = False + wheel_chair: bool = False + av_equipements: bool = False \ No newline at end of file diff --git a/BACKEND/app/services/venue_service.py b/BACKEND/app/services/venue_service.py index 660c15afb..9d49cfd65 100644 --- a/BACKEND/app/services/venue_service.py +++ b/BACKEND/app/services/venue_service.py @@ -2,6 +2,7 @@ from app.model.venue import Venue from app.model.user import User +from app.model.venue_amenities import VenueAmenities def get_venues( db: Session, @@ -76,7 +77,41 @@ def add_venue( db.commit() db.refresh(new_venue) - return new_venue + return { + "message": "Venue created successfully", + "venue_id": new_venue.id + } + +def add_venue_amenities( + db: Session, + venue_id: int, + wifi: bool = False, + kitchen: bool = False, + parking: bool = False, + ac: bool = False, + wheel_chair: bool = False, + av_equipements: bool = False, +): + + amenities = VenueAmenities( + venue_id=venue_id, + wifi=wifi, + kitchen=kitchen, + parking=parking, + ac=ac, + wheel_chair=wheel_chair, + av_equipements=av_equipements, + ) + + + db.add(amenities) + db.commit() + db.refresh(amenities) + + return { + "message": "Amenities added successfully", + "venue_id": amenities.id + } From ade72d431e5504aa82dcd18ffabfa02613d5cbe8 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Sun, 14 Jun 2026 00:46:18 +0530 Subject: [PATCH 14/95] added venue images, cloudinary --- BACKEND/alembic/env.py | 2 +- BACKEND/app/api/venue_routes.py | 41 ++++++++++++++++-- BACKEND/app/core/config.py | 4 ++ BACKEND/app/main.py | 2 +- .../{venue_amenites.py => venue_amenities.py} | 0 BACKEND/app/services/cloudinary_service.py | 42 +++++++++++++++++++ BACKEND/app/services/venue_service.py | 19 +++++++++ 7 files changed, 105 insertions(+), 5 deletions(-) rename BACKEND/app/model/{venue_amenites.py => venue_amenities.py} (100%) create mode 100644 BACKEND/app/services/cloudinary_service.py diff --git a/BACKEND/alembic/env.py b/BACKEND/alembic/env.py index f49484544..82daeac14 100644 --- a/BACKEND/alembic/env.py +++ b/BACKEND/alembic/env.py @@ -26,7 +26,7 @@ # from myapp import mymodel from app.model.user import User from app.model.venue import Venue -from app.model.venue_amenites import VenueAmenities +from app.model.venue_amenities import VenueAmenities from app.model.venue_images import VenueImages # target_metadata = mymodel.Base.metadata diff --git a/BACKEND/app/api/venue_routes.py b/BACKEND/app/api/venue_routes.py index bcd41aff9..a22e4d7f1 100644 --- a/BACKEND/app/api/venue_routes.py +++ b/BACKEND/app/api/venue_routes.py @@ -12,9 +12,15 @@ get_venues, get_venue_details_by_id, add_venue, - add_venue_amenities + add_venue_amenities, + add_venue_images ) +from fastapi import APIRouter, HTTPException, status, UploadFile, File +from typing import List +from app.services.cloudinary_service import upload_images + + router = APIRouter( prefix="/venues", tags=["Venues"] @@ -91,7 +97,7 @@ def venues( try: return add_venue_amenities( db=db, - venue_id=payload.user_id, + venue_id=venue_id, wifi=payload.wifi, kitchen=payload.kitchen, parking=payload.parking, @@ -103,4 +109,33 @@ def venues( raise HTTPException( status_code=400, detail=str(e) - ) \ No newline at end of file + ) + +@router.post("/{venue_id}/images") +async def upload_venue_images( + venue_id: int, + images: List[UploadFile] = File(...), + db: Session = Depends(get_db), +): + try: + images_urls = await upload_images( + images=images, + venue_id=venue_id + ) + print(images_urls) + + return add_venue_images( + db=db, + images_urls=images_urls, + venue_id=venue_id + ) + + + return images_urls + + + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) diff --git a/BACKEND/app/core/config.py b/BACKEND/app/core/config.py index dc23a5e54..0bcda02c5 100644 --- a/BACKEND/app/core/config.py +++ b/BACKEND/app/core/config.py @@ -5,6 +5,10 @@ class Settings(BaseSettings): SECRET_KEY: str ALGORITHM: str = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES: int = 60 + CLOUDINARY_CLOUD_NAME: str + CLOUDINARY_API_KEY: str + CLOUDINARY_API_SECRET: str + CLOUDINARY_FOLDER_NAME: str class Config: env_file = ".env" diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py index d55ab1f97..db56d6017 100644 --- a/BACKEND/app/main.py +++ b/BACKEND/app/main.py @@ -3,7 +3,7 @@ from app.db.database import Base, engine from app.model.user import User from app.model.venue import Venue -from app.model.venue_amenites import VenueAmenities +from app.model.venue_amenities import VenueAmenities from app.model.venue_images import VenueImages from app.api.user_routes import router as user_router diff --git a/BACKEND/app/model/venue_amenites.py b/BACKEND/app/model/venue_amenities.py similarity index 100% rename from BACKEND/app/model/venue_amenites.py rename to BACKEND/app/model/venue_amenities.py diff --git a/BACKEND/app/services/cloudinary_service.py b/BACKEND/app/services/cloudinary_service.py new file mode 100644 index 000000000..a73db07da --- /dev/null +++ b/BACKEND/app/services/cloudinary_service.py @@ -0,0 +1,42 @@ +import cloudinary +import os +from cloudinary.uploader import upload +from fastapi import HTTPException, status, UploadFile +from app.core.config import settings +from typing import List + +cloudinary.config( + cloud_name=settings.CLOUDINARY_CLOUD_NAME, + api_key=settings.CLOUDINARY_API_KEY, + api_secret=settings.CLOUDINARY_API_SECRET, +) + +async def upload_images( + images: List[UploadFile], + venue_id: int +): + try: + + folder = settings.CLOUDINARY_FOLDER_NAME + + uploaded_files = [] + + for image in images: + result = upload( + image.file, + folder=f"{folder}/{venue_id}" + ) + + uploaded_files.append({ + "file_name": image.filename, + "url": result["secure_url"], + "public_id": result["public_id"] + }) + + return uploaded_files + + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error uploading image: {e}" + ) \ No newline at end of file diff --git a/BACKEND/app/services/venue_service.py b/BACKEND/app/services/venue_service.py index 9d49cfd65..70d05d3cd 100644 --- a/BACKEND/app/services/venue_service.py +++ b/BACKEND/app/services/venue_service.py @@ -3,6 +3,8 @@ from app.model.venue import Venue from app.model.user import User from app.model.venue_amenities import VenueAmenities +from app.model.venue_images import VenueImages +from typing import List def get_venues( db: Session, @@ -113,5 +115,22 @@ def add_venue_amenities( "venue_id": amenities.id } +def add_venue_images( + db: Session, + images_urls : List, + venue_id : int +): + for image in images_urls: + add_image_url = VenueImages( + venue_id=venue_id, + image_url=image["url"] + ) + db.add(add_image_url) + db.commit() + db.refresh(add_image_url) + return { + "message": "Images added successfully", + } + \ No newline at end of file From 9d9114c883168b4042a7f4c79a61b6ef56ef7af3 Mon Sep 17 00:00:00 2001 From: nafanpe Date: Mon, 15 Jun 2026 01:02:11 +0530 Subject: [PATCH 15/95] feat: Added Dynamics, Owner Landing Page, responsiveness --- FRONTEND/index.html | 2 +- FRONTEND/package-lock.json | 719 ++++++++++++++++++++++++- FRONTEND/package.json | 3 + FRONTEND/src/App.jsx | 5 + FRONTEND/src/components/Navbar.jsx | 82 ++- FRONTEND/src/components/VenueGrid.jsx | 60 ++- FRONTEND/src/data/VenueCardData.jsx | 23 +- FRONTEND/src/hooks/useAuthForm.jsx | 49 +- FRONTEND/src/hooks/useVenueGrid.jsx | 37 ++ FRONTEND/src/pages/HomePage.jsx | 1 + FRONTEND/src/pages/HostLandingPage.jsx | 305 +++++++++++ FRONTEND/src/pages/LoginPage.jsx | 21 +- FRONTEND/src/pages/VenueListing.jsx | 229 ++++++++ 13 files changed, 1459 insertions(+), 77 deletions(-) create mode 100644 FRONTEND/src/hooks/useVenueGrid.jsx create mode 100644 FRONTEND/src/pages/HostLandingPage.jsx create mode 100644 FRONTEND/src/pages/VenueListing.jsx diff --git a/FRONTEND/index.html b/FRONTEND/index.html index f94d687d3..2c738df8c 100644 --- a/FRONTEND/index.html +++ b/FRONTEND/index.html @@ -1,5 +1,5 @@ - + diff --git a/FRONTEND/package-lock.json b/FRONTEND/package-lock.json index c13598945..aa8f22f24 100644 --- a/FRONTEND/package-lock.json +++ b/FRONTEND/package-lock.json @@ -8,6 +8,9 @@ "name": "frontend", "version": "0.0.0", "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/material": "^9.1.1", "@mynaui/icons-react": "^0.4.2", "@tailwindcss/vite": "^4.3.0", "lucide-react": "^1.17.0", @@ -32,7 +35,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", @@ -88,7 +90,6 @@ "version": "7.29.1", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.0", @@ -122,7 +123,6 @@ "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -132,7 +132,6 @@ "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", - "dev": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.28.6", @@ -164,7 +163,6 @@ "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -174,7 +172,6 @@ "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -208,7 +205,6 @@ "version": "7.29.3", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" @@ -220,11 +216,19 @@ "node": ">=6.0.0" } }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/template": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.28.6", @@ -239,7 +243,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", - "dev": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.29.0", @@ -258,7 +261,6 @@ "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", - "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", @@ -299,6 +301,158 @@ "tslib": "^2.4.0" } }, + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" + }, + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" + } + }, + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" + }, + "node_modules/@emotion/is-prop-valid": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" + } + }, + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" + }, + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" + } + }, + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" + }, + "node_modules/@emotion/styled": { + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" + }, + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } + }, + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", @@ -538,6 +692,225 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@mui/core-downloads-tracker": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-9.1.1.tgz", + "integrity": "sha512-AupmMICbdJHqAh6FfOMaaiiIr7dfEgZJn5DFfiPuGNrbs+ZZy9cD1APwO0TSVBz5j08MJEEY6n7iC76/2wjMEA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/material": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-9.1.1.tgz", + "integrity": "sha512-Wv+gInjrpf99l1Q0oHe0eOWGTnlbkzs5nowClX65KCT/2fyPMwcbFEEkUsOHdpcHhB5UAbz/d7jlwt5ajWVvlA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/core-downloads-tracker": "^9.1.1", + "@mui/system": "^9.1.1", + "@mui/types": "^9.1.1", + "@mui/utils": "^9.1.1", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1", + "react-is": "^19.2.6", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^9.1.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material/node_modules/react-is": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", + "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", + "license": "MIT" + }, + "node_modules/@mui/private-theming": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-9.1.1.tgz", + "integrity": "sha512-oH6c+d6sJ1CZT0Vg2/fHdUQ5zvo9Pn+f+WWk0tlQliHqqIRdN32DZ7UxjalW3LUj4OkHbdWR31biWuLxK9i7Cg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/utils": "^9.1.1", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-9.1.1.tgz", + "integrity": "sha512-neaYKdJfvEG54q8efHLJR7swpHG/gfSv9xGqW5iTSMsubD7yPCPFrhVBt284j1DOF3uZaaDJSHQL7gz6jGF21Q==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-9.1.1.tgz", + "integrity": "sha512-q+aqNa58QZUwmmyUvJKKrStrej+4BcWFw4M0Ug+zRylPIQgR64cqvBnE3QTfLZm4OXulydp8Hl3zwKxMayrdsA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/private-theming": "^9.1.1", + "@mui/styled-engine": "^9.1.1", + "@mui/types": "^9.1.1", + "@mui/utils": "^9.1.1", + "clsx": "^2.1.1", + "csstype": "^3.2.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-9.1.1.tgz", + "integrity": "sha512-Zjt7u8wNvDg40rPTGoL+TnfkpuSKjwubsNSFRH1KAVZLcaV4I3AFNHIFbvH7p4F3alEibSbdd90xAgn5Rnfndg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "9.1.1", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-9.1.1.tgz", + "integrity": "sha512-qSNfnkzZMptaaWFFklpDf4NPJztgwsMDVfM/sSDt+wq4ssYSBhLYwwjuB6eS/+p2IUYbeRzHluzXbw0Zn7aI4A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "@mui/types": "^9.1.1", + "@types/prop-types": "^15.7.15", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.2.6" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils/node_modules/react-is": { + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz", + "integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==", + "license": "MIT" + }, "node_modules/@mynaui/icons-react": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@mynaui/icons-react/-/icons-react-0.4.2.tgz", @@ -581,6 +954,16 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.2.tgz", @@ -1117,11 +1500,22 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" + }, "node_modules/@types/react": { "version": "19.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" @@ -1137,6 +1531,15 @@ "@types/react": "^19.2.0" } }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, "node_modules/@vitejs/plugin-react": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", @@ -1203,6 +1606,21 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, "node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", @@ -1273,6 +1691,15 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001793", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", @@ -1294,6 +1721,15 @@ ], "license": "CC-BY-4.0" }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -1314,6 +1750,31 @@ "url": "https://opencollective.com/express" } }, + "node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.3.tgz", + "integrity": "sha512-vIYeF1u3CjlhAFekPPAk2h/Kv4T3mAkMox5OymRiJQB0spDP10LHvt+K7G9Ny6NuuMAb25/6n1qyUjAcGNf/AA==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1333,14 +1794,12 @@ "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" @@ -1370,6 +1829,16 @@ "node": ">=8" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.361", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", @@ -1390,6 +1859,24 @@ "node": ">=10.13.0" } }, + "node_modules/error-ex": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", @@ -1404,7 +1891,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -1646,6 +2132,12 @@ "node": ">=16.0.0" } }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -1698,6 +2190,15 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -1740,6 +2241,18 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC" }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/hermes-estree": { "version": "0.25.1", "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", @@ -1757,6 +2270,15 @@ "hermes-estree": "0.25.1" } }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -1767,6 +2289,22 @@ "node": ">= 4" } }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -1777,6 +2315,27 @@ "node": ">=0.8.19" } }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -1826,7 +2385,6 @@ "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" @@ -1842,6 +2400,12 @@ "dev": true, "license": "MIT" }, + "node_modules/json-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" + }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", @@ -2142,6 +2706,12 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -2218,7 +2788,6 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true, "license": "MIT" }, "node_modules/nanoid": { @@ -2315,6 +2884,36 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -2335,6 +2934,21 @@ "node": ">=8" } }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2477,6 +3091,52 @@ "react-dom": ">=18" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/rolldown": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", @@ -2555,6 +3215,15 @@ "node": ">=8" } }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -2564,6 +3233,24 @@ "node": ">=0.10.0" } }, + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/tailwindcss": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", diff --git a/FRONTEND/package.json b/FRONTEND/package.json index 772da1a74..5121d6616 100644 --- a/FRONTEND/package.json +++ b/FRONTEND/package.json @@ -10,6 +10,9 @@ "preview": "vite preview" }, "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.1", + "@mui/material": "^9.1.1", "@mynaui/icons-react": "^0.4.2", "@tailwindcss/vite": "^4.3.0", "lucide-react": "^1.17.0", diff --git a/FRONTEND/src/App.jsx b/FRONTEND/src/App.jsx index 1ef721b0d..0ebad17e1 100644 --- a/FRONTEND/src/App.jsx +++ b/FRONTEND/src/App.jsx @@ -1,6 +1,9 @@ import { Routes, Route } from 'react-router-dom'; import HomePage from "./pages/HomePage" import LoginPage from "./pages/LoginPage" +import SpaceListing from "./pages/VenueListing" +import BookMyVenueLanding from "./pages/HostLandingPage" + function App() { @@ -10,6 +13,8 @@ function App() { } /> } /> + } /> + } />
diff --git a/FRONTEND/src/components/Navbar.jsx b/FRONTEND/src/components/Navbar.jsx index 2cbf001c4..cab63eabe 100644 --- a/FRONTEND/src/components/Navbar.jsx +++ b/FRONTEND/src/components/Navbar.jsx @@ -1,16 +1,64 @@ -import { Link } from "react-router-dom" -import { useState } from "react" +import { Link, useLocation, useNavigate } from "react-router-dom" +import { useEffect, useState } from "react" import logo from "../assets/Logo.png" +function ActionButton({role}) { + + const navigate = useNavigate() + + if(role === "owner" || role === "admin"){ + return ( + + ) + + } else if(role === "user") { + return null + + } else { + return ( + + ) + } +} + function NavBar() { const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); - const toggleMenu = () => { setIsMobileMenuOpen(!isMobileMenuOpen); }; + const location = useLocation() + + const isActive = (path) => location.pathname === path + + const role = localStorage.getItem("userRole") + return (
{/* Main Navbar */} @@ -25,21 +73,31 @@ function NavBar() { {/* Desktop Nav Links (Hidden on Mobile) */}
+
- Find Space - + + Find Space + +
+ + {role === "owner" &&
- Host a venue - + + Host a venue + +
+ }
{/* Desktop User Actions (Hidden on Mobile) */} -
- + {/* Mobile Hamburger Toggle (Visible only on Mobile) */} @@ -69,7 +127,7 @@ function NavBar() { Find Space - + Host a venue @@ -133,7 +132,7 @@ function LoginPage() { +

You won't be charged yet

+ + {/* Price Breakdown */} +
+
+ ₹30,000 x 1 session + ₹30,000 +
+
+ Cleaning fee + ₹2,500 +
+
+ Platform fee + ₹1,500 +
+
+ + {/* Total */} +
+ Total before taxes + ₹34,000 +
+ +
+
+
+
+ ); +} \ No newline at end of file From acf059566191ec3958529dcae8a563a73778bd92 Mon Sep 17 00:00:00 2001 From: nafanpe Date: Mon, 15 Jun 2026 01:29:05 +0530 Subject: [PATCH 16/95] fix: image on auth --- FRONTEND/src/components/Navbar.jsx | 2 +- FRONTEND/src/pages/LoginPage.jsx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/FRONTEND/src/components/Navbar.jsx b/FRONTEND/src/components/Navbar.jsx index cab63eabe..023c1c904 100644 --- a/FRONTEND/src/components/Navbar.jsx +++ b/FRONTEND/src/components/Navbar.jsx @@ -2,7 +2,7 @@ import { Link, useLocation, useNavigate } from "react-router-dom" import { useEffect, useState } from "react" import logo from "../assets/Logo.png" -function ActionButton({role}) { + function ActionButton({role}) { const navigate = useNavigate() diff --git a/FRONTEND/src/pages/LoginPage.jsx b/FRONTEND/src/pages/LoginPage.jsx index 88c541a9d..1a3815e40 100644 --- a/FRONTEND/src/pages/LoginPage.jsx +++ b/FRONTEND/src/pages/LoginPage.jsx @@ -1,10 +1,11 @@ import Logo from "../assets/Logo.png" +import stock1 from "../assets/stock1.png" import { useAuthForm } from "../hooks/useAuthForm"; import { Users, Store, ArrowRight, SpinnerOne } from "@mynaui/icons-react"; const LeftPanel = () => (
- Login Illustration From 4fe2b77343d8b40dc618148788b26b61aaa05787 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Tue, 16 Jun 2026 22:12:09 +0530 Subject: [PATCH 17/95] venue status, venue active inactive --- BACKEND/app/api/admin_routes.py | 42 +++++++++++ BACKEND/app/api/venue_routes.py | 48 +++++++++++-- BACKEND/app/model/venue.py | 1 + BACKEND/app/schema/venue.py | 13 +++- BACKEND/app/services/auth_service.py | 2 - BACKEND/app/services/venue_service.py | 100 +++++++++++++++++++++++--- 6 files changed, 187 insertions(+), 19 deletions(-) create mode 100644 BACKEND/app/api/admin_routes.py diff --git a/BACKEND/app/api/admin_routes.py b/BACKEND/app/api/admin_routes.py new file mode 100644 index 000000000..b23b970fe --- /dev/null +++ b/BACKEND/app/api/admin_routes.py @@ -0,0 +1,42 @@ +from app.core.dependencies import get_current_user +from typing import Optional +from app.db.session import get_db + +from fastapi import APIRouter, Depends, Query, HTTPException +from sqlalchemy.orm import Session +from app.schema.venue import ( + VenueApprovalRequest +) +from app.services.venue_service import ( + update_venue_approval_status +) + +from fastapi import APIRouter, HTTPException, status, UploadFile, File +from typing import List +from app.services.cloudinary_service import upload_images + + +router = APIRouter( + prefix="/admin", + tags=["Venues"] +) + +@router.post("/venue/{venue_id}") +def get_venue_by_id( + payload: VenueApprovalRequest, + venue_id: int, + db: Session = Depends(get_db), +): + try: + return update_venue_approval_status( + db, + venue_id, + payload.status, + payload.reason + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + diff --git a/BACKEND/app/api/venue_routes.py b/BACKEND/app/api/venue_routes.py index a22e4d7f1..ff6608c79 100644 --- a/BACKEND/app/api/venue_routes.py +++ b/BACKEND/app/api/venue_routes.py @@ -6,14 +6,16 @@ from sqlalchemy.orm import Session from app.schema.venue import ( VenueDetailsCreate, - VenueAmenitiesCreate + VenueAmenitiesCreate, + VenueActiveStatusRequest ) from app.services.venue_service import ( get_venues, get_venue_details_by_id, add_venue, add_venue_amenities, - add_venue_images + add_venue_images, + update_venue_active_status ) from fastapi import APIRouter, HTTPException, status, UploadFile, File @@ -27,7 +29,7 @@ ) @router.get("/") -def venues( +def get_venues_route( db: Session = Depends(get_db), page_no: int = Query(1, ge=1, description="Page number"), limit: int = Query(20, ge=1, le=100, description="Records per page"), @@ -51,7 +53,7 @@ def venues( ) @router.get("/details/{venue_id}") -def venues( +def get_venue_by_id( venue_id: int, db: Session = Depends(get_db), ): @@ -67,7 +69,7 @@ def venues( ) @router.post("/basic-details") -def venues( +def upload_venue_details( payload: VenueDetailsCreate, db: Session = Depends(get_db), ): @@ -89,7 +91,7 @@ def venues( ) @router.post("/{venue_id}/amenities") -def venues( +def upload_venue_amenities( payload: VenueAmenitiesCreate, venue_id: int, db: Session = Depends(get_db), @@ -139,3 +141,37 @@ async def upload_venue_images( status_code=400, detail=str(e) ) + +@router.patch("/active-status/{venue_id}") +def update_venue_status( + venue_id: int, + payload: VenueActiveStatusRequest, + db: Session = Depends(get_db), +): + try: + return update_venue_active_status( + db=db, + venue_id=venue_id, + status=payload.status, + reason=payload.reason + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + + +@router.put("/") +def edit_venue( + db: Session = Depends(get_db), +): + try: + return edit_venue( + db + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) \ No newline at end of file diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py index 8c5689423..a0261fa7f 100644 --- a/BACKEND/app/model/venue.py +++ b/BACKEND/app/model/venue.py @@ -29,6 +29,7 @@ class Venue(Base): venue_price = Column(Integer, nullable=False) venue_availabilty = Column(String(255), nullable=False) # hourly or daily is_available = Column(Boolean, nullable=False, default=False) + not_available_reason = Column(Text, nullable=True, default="") is_approved = Column(Boolean, nullable=False, default=False) created_at = Column(DateTime(timezone=True), server_default=func.now()) diff --git a/BACKEND/app/schema/venue.py b/BACKEND/app/schema/venue.py index 0833249d5..a6a307ca1 100644 --- a/BACKEND/app/schema/venue.py +++ b/BACKEND/app/schema/venue.py @@ -1,5 +1,5 @@ from pydantic import BaseModel, Field -from typing import Literal +from typing import Literal, Optional class VenueDetailsCreate(BaseModel): user_id: int = Field(..., gt=0) @@ -16,4 +16,13 @@ class VenueAmenitiesCreate(BaseModel): parking: bool = False ac: bool = False wheel_chair: bool = False - av_equipements: bool = False \ No newline at end of file + av_equipements: bool = False + + +class VenueApprovalRequest(BaseModel): + status: Literal["approved", "rejected"] + reason: Optional[str] = None + +class VenueActiveStatusRequest(BaseModel): + status: Literal["active", "inactive"] + reason: Optional[str] = None \ No newline at end of file diff --git a/BACKEND/app/services/auth_service.py b/BACKEND/app/services/auth_service.py index dfeb78760..1cd3340eb 100644 --- a/BACKEND/app/services/auth_service.py +++ b/BACKEND/app/services/auth_service.py @@ -45,9 +45,7 @@ def signup( ) db.add(user) - db.commit() - db.refresh(user) return { diff --git a/BACKEND/app/services/venue_service.py b/BACKEND/app/services/venue_service.py index 70d05d3cd..52d60813b 100644 --- a/BACKEND/app/services/venue_service.py +++ b/BACKEND/app/services/venue_service.py @@ -13,17 +13,22 @@ def get_venues( action : str, location : str, avaialability : str -): - offset = (page_no - 1) * limit +): + try: + offset = (page_no - 1) * limit - venues = db.query(Venue).offset(offset).limit(limit).all() + venues = db.query(Venue).offset(offset).limit(limit).all() - if not venues: - return { - "message": "venues are not added" - } + if not venues: + return { + "message": "venues are not added" + } + + return venues + + except Exception as e: + raise Exception(f"Error occurred while fetching venues: {str(e)}") - return venues def get_venue_details_by_id( db: Session, @@ -133,4 +138,81 @@ def add_venue_images( return { "message": "Images added successfully", } - \ No newline at end of file + +def update_venue_approval_status( + db: Session, + venue_id: int, + status: str, + reason: str +): + venue = ( + db.query(Venue) + .filter(Venue.id == venue_id) + .first() + ) + + if not venue: + raise Exception("Invalid venue") + + if status.lower() == "approved": + venue.is_approved = True + elif status.lower() == "rejected": + venue.is_approved = False + + ## reason + ## rejection schema to store rejected description plus venue_id + ## shoot mail to venue owner mentioning rejection + + else: + raise Exception("Status must be either 'approved' or 'rejected'") + + db.commit() + db.refresh(venue) + + return { + "message": f"Venue {status.lower()} successfully", + "venue_id": venue.id, + "is_approved": venue.is_approved + } + + +def update_venue_active_status( + db: Session, + venue_id: int, + status: str, + reason: str = None +): + venue = ( + db.query(Venue) + .filter(Venue.id == venue_id) + .first() + ) + + if not venue: + raise Exception("Invalid venue") + + if status.lower() == "active": + venue.is_active = True + + elif status.lower() == "inactive": + venue.is_active = False + venue.not_available_reason = reason + + else: + raise Exception("Status must be either 'active' or 'inactive'") + + db.commit() + db.refresh(venue) + + return { + "message": f"Venue {status.lower()} successfully", + "venue_id": venue.id, + "is_active": venue.is_active + } + + +def edit_venue(): + return { + "message": f"Venue edited successfully", + # "venue_id": venue.id, + } \ No newline at end of file From 5b3d4344a88c211eb6eda5af1414b980b5213db8 Mon Sep 17 00:00:00 2001 From: nafanpe Date: Wed, 17 Jun 2026 15:20:16 +0530 Subject: [PATCH 18/95] feat: Add Calender/Session UI in VenueListing Page --- FRONTEND/package-lock.json | 444 +++++++++++++++--------- FRONTEND/package.json | 2 + FRONTEND/src/components/FormBooking.jsx | 102 ++++++ FRONTEND/src/components/Navbar.jsx | 5 +- FRONTEND/src/components/VenueGrid.jsx | 18 +- FRONTEND/src/data/VenueCardData.jsx | 30 ++ FRONTEND/src/hooks/useAuthForm.jsx | 6 +- FRONTEND/src/pages/VenueListing.jsx | 29 +- 8 files changed, 427 insertions(+), 209 deletions(-) create mode 100644 FRONTEND/src/components/FormBooking.jsx diff --git a/FRONTEND/package-lock.json b/FRONTEND/package-lock.json index aa8f22f24..bf7f007ab 100644 --- a/FRONTEND/package-lock.json +++ b/FRONTEND/package-lock.json @@ -13,8 +13,10 @@ "@mui/material": "^9.1.1", "@mynaui/icons-react": "^0.4.2", "@tailwindcss/vite": "^4.3.0", + "js-cookie": "^3.0.8", "lucide-react": "^1.17.0", "react": "^19.2.6", + "react-calendar": "^6.0.1", "react-dom": "^19.2.6", "react-router-dom": "^7.15.1", "tailwindcss": "^4.3.0" @@ -32,12 +34,12 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", - "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -46,9 +48,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", - "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "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": { @@ -56,21 +58,21 @@ } }, "node_modules/@babel/core": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", - "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "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.0", - "@babel/generator": "^7.29.0", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.29.0", - "@babel/types": "^7.29.0", + "@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", @@ -87,13 +89,13 @@ } }, "node_modules/@babel/generator": { - "version": "7.29.1", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", - "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz", + "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.29.0", - "@babel/types": "^7.29.0", + "@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" @@ -103,14 +105,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "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.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@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" @@ -120,37 +122,37 @@ } }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "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==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "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.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -160,27 +162,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "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==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "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==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "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": { @@ -188,26 +190,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.29.2", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", - "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "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.28.6", - "@babel/types": "^7.29.0" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.29.3", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", - "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "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==", "license": "MIT", "dependencies": { - "@babel/types": "^7.29.0" + "@babel/types": "^7.29.7" }, "bin": { "parser": "bin/babel-parser.js" @@ -226,31 +228,31 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@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.0", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", - "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz", + "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.29.0", - "@babel/generator": "^7.29.0", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.29.0", - "@babel/template": "^7.28.6", - "@babel/types": "^7.29.0", + "@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": { @@ -258,13 +260,13 @@ } }, "node_modules/@babel/types": { - "version": "7.29.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", - "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz", + "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -946,9 +948,9 @@ } }, "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==", + "version": "0.133.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.133.0.tgz", + "integrity": "sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" @@ -965,9 +967,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.3.tgz", + "integrity": "sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==", "cpu": [ "arm64" ], @@ -981,9 +983,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.3.tgz", + "integrity": "sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==", "cpu": [ "arm64" ], @@ -997,9 +999,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.3.tgz", + "integrity": "sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==", "cpu": [ "x64" ], @@ -1013,9 +1015,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.3.tgz", + "integrity": "sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==", "cpu": [ "x64" ], @@ -1029,9 +1031,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.3.tgz", + "integrity": "sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==", "cpu": [ "arm" ], @@ -1045,12 +1047,15 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.3.tgz", + "integrity": "sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==", "cpu": [ "arm64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1061,12 +1066,15 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.3.tgz", + "integrity": "sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==", "cpu": [ "arm64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1077,12 +1085,15 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.3.tgz", + "integrity": "sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==", "cpu": [ "ppc64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1093,12 +1104,15 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.3.tgz", + "integrity": "sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==", "cpu": [ "s390x" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1109,12 +1123,15 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.3.tgz", + "integrity": "sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==", "cpu": [ "x64" ], + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1125,12 +1142,15 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.3.tgz", + "integrity": "sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==", "cpu": [ "x64" ], + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1141,9 +1161,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.3.tgz", + "integrity": "sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==", "cpu": [ "arm64" ], @@ -1157,9 +1177,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.3.tgz", + "integrity": "sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==", "cpu": [ "wasm32" ], @@ -1175,9 +1195,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.3.tgz", + "integrity": "sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==", "cpu": [ "arm64" ], @@ -1191,9 +1211,9 @@ } }, "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==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.3.tgz", + "integrity": "sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==", "cpu": [ "x64" ], @@ -1566,6 +1586,15 @@ } } }, + "node_modules/@wojtekmaj/date-utils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@wojtekmaj/date-utils/-/date-utils-2.0.2.tgz", + "integrity": "sha512-Do66mSlSNifFFuo3l9gNKfRMSFi26CRuQMsDJuuKO/ekrDWuTTtE4ZQxoFCUOG+NgxnpSeBq/k5TY8ZseEzLpA==", + "license": "MIT", + "funding": { + "url": "https://github.com/wojtekmaj/date-utils?sponsor=1" + } + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -1632,9 +1661,9 @@ } }, "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==", + "version": "2.10.37", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.37.tgz", + "integrity": "sha512-girxaJ7WZssDOFhzCGZTDKoTa1gk6A1TbflaYTpykLJ4UU9Fz9kx1aREM8JCuoVHbL8X8T/mJg7w2oYSq72Oig==", "dev": true, "license": "Apache-2.0", "bin": { @@ -1701,9 +1730,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001793", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", - "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "version": "1.0.30001799", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001799.tgz", + "integrity": "sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==", "dev": true, "funding": [ { @@ -1840,9 +1869,9 @@ } }, "node_modules/electron-to-chromium": { - "version": "1.5.361", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.361.tgz", - "integrity": "sha512-Q6Hts7N9FnJc5LeGRINFvLhCI9xZmNtTDe5ZbcVezQz7cU4a8Aua3GH1b8J2XY8Al9PF+OCwYqhgsOOheMdvkA==", + "version": "1.5.373", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.373.tgz", + "integrity": "sha512-G2Hym8JIf/QreuseqkDibgH8Ci8KfJzqGDKdakbhSx9UltwRBH2cBLAWU/lBX0sCdv0TlhyxQyDCnSfxgMWsjA==", "dev": true, "license": "ISC" }, @@ -2209,6 +2238,18 @@ "node": ">=6.9.0" } }, + "node_modules/get-user-locale": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-user-locale/-/get-user-locale-3.0.0.tgz", + "integrity": "sha512-iJfHSmdYV39UUBw7Jq6GJzeJxUr4U+S03qdhVuDsR9gCEnfbqLy9gYDJFBJQL1riqolFUKQvx36mEkp2iGgJ3g==", + "license": "MIT", + "dependencies": { + "memoize": "^10.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/get-user-locale?sponsor=1" + } + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2375,6 +2416,12 @@ "jiti": "lib/jiti-cli.mjs" } }, + "node_modules/js-cookie": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.8.tgz", + "integrity": "sha512-yeJd4aNAdYZQjaon2bpD/Gb0B/omw7HQOsynXXcOiWVCacbBcPlgn8S/d1X6blFSaHao7ozqtW7NZW19xpCtIw==", + "license": "MIT" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -2768,6 +2815,33 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/memoize": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/memoize/-/memoize-10.2.0.tgz", + "integrity": "sha512-DeC6b7QBrZsRs3Y02A6A7lQyzFbsQbqgjI6UW0GigGWV+u1s25TycMr0XHZE4cJce7rY/vyw2ctMQqfDkIhUEA==", + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/memoize?sponsor=1" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/minimatch": { "version": "10.2.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", @@ -2816,9 +2890,9 @@ "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==", + "version": "2.0.47", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.47.tgz", + "integrity": "sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==", "dev": true, "license": "MIT", "engines": { @@ -3035,6 +3109,31 @@ "node": ">=0.10.0" } }, + "node_modules/react-calendar": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/react-calendar/-/react-calendar-6.0.1.tgz", + "integrity": "sha512-b8E61W7qk/He9XEbtbQBjnALPuGmxeglsotgZyAShqN1vHMzXWjl4g7WI5tRF93RE4Wbo0c0BKN3vTQhrBojpg==", + "license": "MIT", + "dependencies": { + "@wojtekmaj/date-utils": "^2.0.2", + "clsx": "^2.0.0", + "get-user-locale": "^3.0.0", + "warning": "^4.0.0" + }, + "funding": { + "url": "https://github.com/wojtekmaj/react-calendar?sponsor=1" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/react-dom": { "version": "19.2.6", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", @@ -3138,12 +3237,12 @@ } }, "node_modules/rolldown": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.2.tgz", - "integrity": "sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==", + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.3.tgz", + "integrity": "sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==", "license": "MIT", "dependencies": { - "@oxc-project/types": "=0.132.0", + "@oxc-project/types": "=0.133.0", "@rolldown/pluginutils": "^1.0.0" }, "bin": { @@ -3153,21 +3252,21 @@ "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" + "@rolldown/binding-android-arm64": "1.0.3", + "@rolldown/binding-darwin-arm64": "1.0.3", + "@rolldown/binding-darwin-x64": "1.0.3", + "@rolldown/binding-freebsd-x64": "1.0.3", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.3", + "@rolldown/binding-linux-arm64-gnu": "1.0.3", + "@rolldown/binding-linux-arm64-musl": "1.0.3", + "@rolldown/binding-linux-ppc64-gnu": "1.0.3", + "@rolldown/binding-linux-s390x-gnu": "1.0.3", + "@rolldown/binding-linux-x64-gnu": "1.0.3", + "@rolldown/binding-linux-x64-musl": "1.0.3", + "@rolldown/binding-openharmony-arm64": "1.0.3", + "@rolldown/binding-wasm32-wasi": "1.0.3", + "@rolldown/binding-win32-arm64-msvc": "1.0.3", + "@rolldown/binding-win32-x64-msvc": "1.0.3" } }, "node_modules/scheduler": { @@ -3271,9 +3370,9 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.16", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", - "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", "license": "MIT", "dependencies": { "fdir": "^6.5.0", @@ -3348,16 +3447,16 @@ } }, "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==", + "version": "8.0.16", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.16.tgz", + "integrity": "sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==", "license": "MIT", "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.4", "postcss": "^8.5.15", - "rolldown": "1.0.2", - "tinyglobby": "^0.2.16" + "rolldown": "1.0.3", + "tinyglobby": "^0.2.17" }, "bin": { "vite": "bin/vite.js" @@ -3424,6 +3523,15 @@ } } }, + "node_modules/warning": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", + "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/FRONTEND/package.json b/FRONTEND/package.json index 5121d6616..ba2803b13 100644 --- a/FRONTEND/package.json +++ b/FRONTEND/package.json @@ -15,8 +15,10 @@ "@mui/material": "^9.1.1", "@mynaui/icons-react": "^0.4.2", "@tailwindcss/vite": "^4.3.0", + "js-cookie": "^3.0.8", "lucide-react": "^1.17.0", "react": "^19.2.6", + "react-calendar": "^6.0.1", "react-dom": "^19.2.6", "react-router-dom": "^7.15.1", "tailwindcss": "^4.3.0" diff --git a/FRONTEND/src/components/FormBooking.jsx b/FRONTEND/src/components/FormBooking.jsx new file mode 100644 index 000000000..3b5e3b530 --- /dev/null +++ b/FRONTEND/src/components/FormBooking.jsx @@ -0,0 +1,102 @@ +import { useState } from 'react'; +import Calendar from 'react-calendar'; +import 'react-calendar/dist/Calendar.css'; + +export default function FormBooking ({venue}) { + + const [selectedDate, setSelectedDate] = useState(new Date()) + const [selectedSession, setSelectedSession] = useState("") + + const handleChange = (e) => { + setSelectedSession(e.target.value) + } + + return ( +
+ + {/* 1. Calendar Widget (Optional: added a soft border to match the inputs) */} +
+ +
+ + {/* 2. Unified Session & Time Slot Card */} +
+ + {/* Select Wrapper */} +
+
+ Session +
+ + + + {/* FIX: SVG is now positioned specifically inside the select area */} + + + +
+ + {/* Hourly Slots - Now seamlessly attached to the bottom of the select */} + {selectedSession === "Hourly" && ( +
+ {venue.availtime && venue.availtime.length > 0 ? ( +
+ {venue.availtime.map((time) => ( + + ))} +
+ ) : ( +
+ No hourly slots available for this venue. +
+ )} +
+ )} + {selectedSession === "Daily" && ( +
+ {venue.availdays && venue.availdays.length > 0 ? ( +
+ {venue.availdays.map((day) => ( + + ))} +
+ ) : ( +
+ No specific daily slots listed for this venue. +
+ )} +
+ )} +
+
+ ) +} \ No newline at end of file diff --git a/FRONTEND/src/components/Navbar.jsx b/FRONTEND/src/components/Navbar.jsx index 023c1c904..e9adff210 100644 --- a/FRONTEND/src/components/Navbar.jsx +++ b/FRONTEND/src/components/Navbar.jsx @@ -1,6 +1,7 @@ import { Link, useLocation, useNavigate } from "react-router-dom" -import { useEffect, useState } from "react" +import { useState } from "react" import logo from "../assets/Logo.png" +import Cookies from 'js-cookie'; function ActionButton({role}) { @@ -57,7 +58,7 @@ function NavBar() { const isActive = (path) => location.pathname === path - const role = localStorage.getItem("userRole") + const role = Cookies.get("userRole") return (
diff --git a/FRONTEND/src/components/VenueGrid.jsx b/FRONTEND/src/components/VenueGrid.jsx index a911d2046..ec1af5a73 100644 --- a/FRONTEND/src/components/VenueGrid.jsx +++ b/FRONTEND/src/components/VenueGrid.jsx @@ -66,7 +66,7 @@ function VenueCard({ venue }) { export default function VenueGrid() { - // const {venues, isLoading, error} = useVenueGrid(); + const {venues, isLoading, error} = useVenueGrid(); const Data = VENUE_DATA // 'venues' for fetched api data [API INTEGRATION PHASE] // if(error){ @@ -80,14 +80,14 @@ export default function VenueGrid() { // ) // } - // if(isLoading){ - // return ( - //
- // - //

Loading your spaces...

- //
- // ) - // } + if(isLoading){ + return ( +
+ +

Loading your spaces...

+
+ ) + } return (
diff --git a/FRONTEND/src/data/VenueCardData.jsx b/FRONTEND/src/data/VenueCardData.jsx index 05d1fd407..19ea105eb 100644 --- a/FRONTEND/src/data/VenueCardData.jsx +++ b/FRONTEND/src/data/VenueCardData.jsx @@ -10,6 +10,12 @@ export const VENUE_DATA = [ rating: "4.9", imageUrl: "https://images.unsplash.com/photo-1572025442646-866d16c84a54?auto=format&fit=crop&q=80&w=800", isLarge: true, + availtime: [ + "10am - 1pm", "1pm - 4pm", "5pm - 10pm" + ], + availdays: [ + "Monday", "Tuesday", "Wednesday" + ], }, { id: 2, @@ -19,6 +25,12 @@ export const VENUE_DATA = [ rating: "4.9", capacity: "210", imageUrl: "https://images.unsplash.com/photo-1497366216548-37526070297c?auto=format&fit=crop&q=80&w=600", + availtime: [ + "10am - 1pm", "1pm - 4pm", "5pm - 10pm" + ], + availdays: [ + "Monday", "Tuesday", "Wednesday" + ], }, { id: 3, @@ -28,6 +40,12 @@ export const VENUE_DATA = [ rating: "4.8", capacity: "110", imageUrl: "https://www.greenply.com:5001/originalfile1769165698904-875.jpg", + availtime: [ + "10am - 1pm", "1pm - 4pm", "5pm - 10pm" + ], + availdays: [ + "Monday", "Tuesday", "Wednesday" + ], }, { id: 4, @@ -37,6 +55,12 @@ export const VENUE_DATA = [ rating: "4.7", capacity: "40", imageUrl: "https://images.unsplash.com/photo-1505691938895-1758d7feb511?auto=format&fit=crop&q=80&w=600", + availtime: [ + "10am - 1pm", "1pm - 4pm", "5pm - 10pm" + ], + availdays: [ + "Monday", "Tuesday", "Wednesday" + ], }, { id: 5, @@ -46,5 +70,11 @@ export const VENUE_DATA = [ rating: "4.9", capacity: "20", imageUrl: "https://images.unsplash.com/photo-1517502884422-41eaead166d4?auto=format&fit=crop&q=80&w=600", + availtime: [ + "10am - 1pm", "1pm - 4pm", "5pm - 10pm" + ], + availdays: [ + "Monday", "Tuesday", "Wednesday" + ], } ]; \ No newline at end of file diff --git a/FRONTEND/src/hooks/useAuthForm.jsx b/FRONTEND/src/hooks/useAuthForm.jsx index 229f9fadd..c60b886bd 100644 --- a/FRONTEND/src/hooks/useAuthForm.jsx +++ b/FRONTEND/src/hooks/useAuthForm.jsx @@ -1,7 +1,5 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; -import Alert from '@mui/material/Alert'; -import { CheckDiamond } from "@mynaui/icons-react"; export const useAuthForm = () => { const navigate = useNavigate(); @@ -77,8 +75,8 @@ export const useAuthForm = () => { console.log("Success! Data sent to server: ", data); if(data.access_token){ - localStorage.setItem('authToken', data.access_token); - localStorage.setItem('userRole', data.user.role) + Cookies.set("authToken", data.access_token, {expires: 30, secure: true, sameSite: 'Lax'}) + Cookies.set('userRole', data.user.role, {expires: 30, secure: true, sameSite: 'Lax'}) } setSuccessMessage(isLoginView ? 'Logged in successfully!' : 'Account created successfully!') diff --git a/FRONTEND/src/pages/VenueListing.jsx b/FRONTEND/src/pages/VenueListing.jsx index a26734601..35eb8d10d 100644 --- a/FRONTEND/src/pages/VenueListing.jsx +++ b/FRONTEND/src/pages/VenueListing.jsx @@ -9,6 +9,8 @@ import { } from '@mynaui/icons-react'; import { useParams } from 'react-router-dom'; import { VENUE_DATA } from '../data/VenueCardData'; +import FormBooking from "../components/FormBooking" + export default function SpaceListing() { @@ -166,32 +168,7 @@ export default function SpaceListing() {
{/* Form Inputs */} -
-
-
-
Check-in
-
Add date
-
-
-
Check-out
-
Add date
-
-
-
-
-
Session
- -
- {/* dropdown arrow */} - - - -
-
+ {/* Submit Button */} - ))} -
- ) : ( -
- No specific daily slots listed for this venue. +
+ {venue.availdays && venue.availdays.length > 0 ? ( +
+ {venue.availdays.map((day) => ( + + ))} +
+ ) : ( +
+ No specific daily slots listed for this venue. +
+ )} +
+ )}
- )} -
- )} -
-
- ) +
+ ) } \ No newline at end of file diff --git a/FRONTEND/src/components/VenueGrid.jsx b/FRONTEND/src/components/VenueGrid.jsx index ec1af5a73..df9be0516 100644 --- a/FRONTEND/src/components/VenueGrid.jsx +++ b/FRONTEND/src/components/VenueGrid.jsx @@ -29,14 +29,14 @@ function VenueCard({ venue }) { const imageSize = venue.isLarge ? "h-144" : "h-48"; // Variables to change in API INTERGRATION PHASE - const venue_name = venue.title - const venue_image = venue.imageUrl - const venue_details = venue.details - const venue_price = venue.price + const venue_name = venue.venue_name + const venue_image = venue.image + const venue_details = venue.venue_description + const venue_price = venue.venue_price return (
navigate(`/venues/${venue.id}`)} - className={`bg-white rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 ease-in-out cursor-pointer ${gridSpan}`} + className={`bg-[#f9f9f7] rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 ease-in-out cursor-pointer ${gridSpan}`} > - //
- //

Oops! Something went wrong.

- //

{error.message}

- //
- //
- // ) - // } + if(error){ + return ( +
+
+

Oops! Something went wrong.

+

{error.message}

+
+
+ ) + } if(isLoading){ return ( @@ -91,10 +91,10 @@ export default function VenueGrid() { return (
- +
{Data.map((venue) => ( diff --git a/FRONTEND/src/hooks/useAuthForm.jsx b/FRONTEND/src/hooks/useAuthForm.jsx index c60b886bd..3a6e1bdfa 100644 --- a/FRONTEND/src/hooks/useAuthForm.jsx +++ b/FRONTEND/src/hooks/useAuthForm.jsx @@ -1,5 +1,7 @@ import { useState } from 'react'; import { useNavigate } from 'react-router-dom'; +import Cookies from 'js-cookie'; +import apiService from "../services/apiService" export const useAuthForm = () => { const navigate = useNavigate(); @@ -25,76 +27,77 @@ export const useAuthForm = () => { } const toggleView = (e) => { - e.preventDefault(); - setIsLoginView(!isLoginView); - setError(''); - setSuccessMessage('') - setFormData({ name: '', email: '', password: ''}); + if(e){ + e.preventDefault(); + } + setIsLoginView(!isLoginView); + setError(''); + setSuccessMessage(''); + setFormData({ name: '', email: '', password: ''}); }; const handleSubmit = async (e) => { - e.preventDefault(); - setError('') - - // validation for both login and sign up forms - if(!formData.email || !formData.password){ - return setError('Please fill in all required fields.'); - } - - // validation for sign up form only - if(!isLoginView){ - if(!formData.name) return setError('Please fill Your Name'); - if(formData.password.length < 6) return setError('Password must be at least 6 characters long.'); - } - - setIsLoading(true); + e.preventDefault(); + setError(''); - try{ - const BASE_URL = "https://unsaving-channing-sisterly.ngrok-free.dev" - - const endpoint = isLoginView ? "/auth/login" : "/auth/signup"; - - const payload = isLoginView - ? {email: formData.email, password: formData.password} - : {name: formData.name, email: formData.email, password: formData.password, role: role} - - const response = await fetch(BASE_URL + endpoint, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(payload) - }) - - const data = await response.json(); - - if(!response.ok){ - throw new Error(data.message || "Something went wrong. Please try again."); + // validation for both login and sign up forms + if(!formData.email || !formData.password){ + return setError('Please fill in all required fields.'); } - - console.log("Success! Data sent to server: ", data); - if(data.access_token){ - Cookies.set("authToken", data.access_token, {expires: 30, secure: true, sameSite: 'Lax'}) - Cookies.set('userRole', data.user.role, {expires: 30, secure: true, sameSite: 'Lax'}) + // validation for sign up form only + if(!isLoginView){ + if(!formData.name) return setError('Please fill Your Name'); + if(formData.password.length < 6) return setError('Password must be at least 6 characters long.'); } - setSuccessMessage(isLoginView ? 'Logged in successfully!' : 'Account created successfully!') - - setTimeout(() => { - if (data.user.role == "user"){ - navigate("/") - } else if(data.user.role == "admin") { - navigate("/admin") - } else if(data.user.role == "owner"){ - navigate("/host") + setIsLoading(true); + + try{ + let data; + + if (isLoginView){ + data = await apiService.login({ + email: formData.email, + password: formData.password + }) + } else { + data = await apiService.signup({ + name: formData.name, + email: formData.email, + password: formData.password, + role: role + }) } - }, 1000); + if(data.access_token){ + Cookies.set("authToken", data.access_token, {expires: 30, secure: true, sameSite: 'Lax'}) + Cookies.set('userRole', data.user.role, {expires: 30, secure: true, sameSite: 'Lax'}) + } + + setSuccessMessage(isLoginView ? 'Logged in successfully!' : 'Account created successfully!') + + setTimeout(() => { + if(!isLoginView){ + toggleView(); + setSuccessMessage("Please Login with your new account."); + return; + } + + if (data.user.role == "user"){ + navigate("/") + console.log(data.user.role) + } else if(data.user.role == "admin") { + navigate("/admin") + } else if(data.user.role == "owner"){ + navigate("/host") + } + }, 1000); } catch (err) { - console.error("Error during form submission:", err); - setError(err.message) + console.error("Error during form submission:", err.response?.data); + const errMessage = err.response?.data?.detail || err.message || "An error Occured"; + setError(errMessage); } finally { setIsLoading(false); @@ -106,5 +109,6 @@ export const useAuthForm = () => { isLoginView, toggleView, formData, handleChange, error, isLoading, handleSubmit, + successMessage } } \ No newline at end of file diff --git a/FRONTEND/src/hooks/useVenueGrid.jsx b/FRONTEND/src/hooks/useVenueGrid.jsx index 43bc68489..2dcd69a5c 100644 --- a/FRONTEND/src/hooks/useVenueGrid.jsx +++ b/FRONTEND/src/hooks/useVenueGrid.jsx @@ -1,4 +1,5 @@ import { useState, useEffect } from "react" +import apiService from "../services/apiService" export const useVenueGrid = () => { const [venues, setVenues] = useState([]) @@ -8,21 +9,13 @@ export const useVenueGrid = () => { useEffect(() => { const fetchVenues = async () => { try{ - const BASE_URL = "https://unsaving-channing-sisterly.ngrok-free.dev" + const data = await apiService.getAllVenues() + setVenues(data) - const response = await fetch(`${BASE_URL}/venues`) - - if(!response.ok){ - throw new Error("Failed to Fetch Venues from server!") - } - - const data = await response.json() - - setVenues(data) - - } catch(err) { + } catch(err) { console.error("Something Went Wrong!", err) setError(err) + } finally { setIsLoading(false) } diff --git a/FRONTEND/src/pages/LoginPage.jsx b/FRONTEND/src/pages/LoginPage.jsx index 1a3815e40..e73e94638 100644 --- a/FRONTEND/src/pages/LoginPage.jsx +++ b/FRONTEND/src/pages/LoginPage.jsx @@ -1,164 +1,185 @@ -import Logo from "../assets/Logo.png" -import stock1 from "../assets/stock1.png" +import Logo from "../assets/Logo.png"; +import stock1 from "../assets/stock1.png"; import { useAuthForm } from "../hooks/useAuthForm"; import { Users, Store, ArrowRight, SpinnerOne } from "@mynaui/icons-react"; +// --- Extracted Components --- + +const InputField = ({ label, name, type = "text", value, onChange, placeholder }) => ( +
+ + +
+); + +const RoleButton = ({ isActive, onClick, icon: Icon, title, subtitle }) => ( + +); + const LeftPanel = () => (
- Login Illustration -
Logo

Spaces that Bring People Together

-

Join a Community of organizers and hosts
- dedicated to creating memorable Experiences!

+

+ Join a Community of organizers and hosts
+ dedicated to creating memorable Experiences! +

-) +); + +// --- Main Page Component --- -function LoginPage() { +export default function LoginPage() { const { - role, setRole, isLoginView, toggleView, + role, setRole, isLoginView, toggleView, formData, handleChange, error, isLoading, handleSubmit, successMessage } = useAuthForm(); + // Extracting conditional text makes the JSX below much easier to read + const headerTitle = isLoginView ? "Log In to Your Account" : "Create Your Account"; + const headerSubtitle = isLoginView + ? "Welcome back! Please enter your details." + : "Join us and start creating unforgettable experiences!"; + const submitText = isLoginView ? "Log In" : "Join the Community"; + const footerText = isLoginView ? "Don't have an account? " : "Already have an account? "; + const footerActionText = isLoginView ? "Sign Up" : "Log In"; + return (
- - - {/* Right Panel */} +
- {/* Form Header */} + {/* Header */}
-

{ - isLoginView ? "Log In to Your Account" : "Create Your Account" - }

-

{ - isLoginView ? "Welcome back! Please enter your details." : "Join us and start creating unforgettable experiences!" - }

+

{headerTitle}

+

{headerSubtitle}

- {/* Role Selection Toggle */} + {/* Role Selection */} {!isLoginView && (
- {/* user Button */} - - - {/* Venue Owner Button */} - + setRole("user")} + icon={Users} + title="The User" + subtitle="Guest" + /> + setRole("owner")} + icon={Store} + title="The Venue Owner" + subtitle="Host" + />
)} - {/* Form Error Message Display */} + {/* Error Message */} {error && (
{error}
)} - {/* Input Form */} -
- {!isLoginView && ( -
- - -
- )} - -
- - + {/* Success Message */} + {successMessage && ( +
+ {successMessage}
- -
- - + {!isLoginView && ( + -
+ )} + + + + - {/* Footer Link */} + {/* Footer */}

- {isLoginView ? "Don't have an account? " : "Already have an account? "} - - {isLoginView ? 'Sign Up' : "Log In"} - + {footerText} +

- ) -} - -export default LoginPage \ No newline at end of file + ); +} \ No newline at end of file diff --git a/FRONTEND/src/pages/VenueListing.jsx b/FRONTEND/src/pages/VenueListing.jsx index 35eb8d10d..422a24a66 100644 --- a/FRONTEND/src/pages/VenueListing.jsx +++ b/FRONTEND/src/pages/VenueListing.jsx @@ -11,7 +11,6 @@ import { useParams } from 'react-router-dom'; import { VENUE_DATA } from '../data/VenueCardData'; import FormBooking from "../components/FormBooking" - export default function SpaceListing() { const {id} = useParams(); diff --git a/FRONTEND/src/services/apiService.js b/FRONTEND/src/services/apiService.js new file mode 100644 index 000000000..62688aa80 --- /dev/null +++ b/FRONTEND/src/services/apiService.js @@ -0,0 +1,18 @@ +import axiosInstance from "./axiosInstance"; + +const apiService = { + login: async (payload) => { + const response = await axiosInstance.post('/auth/login', payload) + return response.data + }, + signup: async (payload) => { + const response = await axiosInstance.post('/auth/signup', payload) + return response.data + }, + getAllVenues: async () => { + const response = await axiosInstance.get('/venues/') + return response.data + } +} + +export default apiService \ No newline at end of file diff --git a/FRONTEND/src/services/axiosInstance.js b/FRONTEND/src/services/axiosInstance.js new file mode 100644 index 000000000..4c82ca1ed --- /dev/null +++ b/FRONTEND/src/services/axiosInstance.js @@ -0,0 +1,29 @@ +import axios from 'axios' +import Cookies from 'js-cookie'; + +const axiosInstance = axios.create({ + baseURL: import.meta.env.VITE_BASE_URL, +}) + +axiosInstance.interceptors.request.use( + (config) => { + const token = Cookies.get("authToken") + const role = Cookies.get("userRole") + + if(token){ + config.headers["Authorization"] = `Bearer ${token}` + } + if (role){ + config.headers["role"] = role + } + + config.headers["ngrok-skip-browser-warning"] = 'true'; + + return config + }, + (error) => { + return Promise.reject(error) + } +) + +export default axiosInstance \ No newline at end of file diff --git a/FRONTEND/vite.config.js b/FRONTEND/vite.config.js index 7575138c1..2ad2ff771 100644 --- a/FRONTEND/vite.config.js +++ b/FRONTEND/vite.config.js @@ -5,4 +5,5 @@ import react from '@vitejs/plugin-react' // https://vite.dev/config/ export default defineConfig({ plugins: [react(), tailwindcss()], + }) From fc36aae287a26e6b98ef827c33825c1b27e42e6a Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Thu, 18 Jun 2026 17:07:53 +0530 Subject: [PATCH 22/95] venue avaialability added --- BACKEND/app/api/venue_routes.py | 62 +++++++-- BACKEND/app/main.py | 1 + BACKEND/app/model/user.py | 3 +- BACKEND/app/model/venue.py | 4 +- BACKEND/app/model/venue_availability.py | 44 +++++++ BACKEND/app/schema/venue.py | 8 ++ BACKEND/app/services/venue_service.py | 160 ++++++++++++++++++++++-- 7 files changed, 260 insertions(+), 22 deletions(-) create mode 100644 BACKEND/app/model/venue_availability.py diff --git a/BACKEND/app/api/venue_routes.py b/BACKEND/app/api/venue_routes.py index f5b991e3f..09067ac90 100644 --- a/BACKEND/app/api/venue_routes.py +++ b/BACKEND/app/api/venue_routes.py @@ -9,7 +9,8 @@ VenueAmenitiesCreate, VenueAmenitiesUpdate, VenueActiveStatusRequest, - VenueDetailsUpdate + VenueDetailsUpdate, + VenueAvailabilityCreate ) from app.services.venue_service import ( get_venues, @@ -19,7 +20,9 @@ add_venue_images, update_venue_active_status, edit_venue, - edit_venue_amenities + edit_venue_amenities, + search_venues, + add_venue_availability ) from fastapi import APIRouter, HTTPException, status, UploadFile, File @@ -37,18 +40,12 @@ def get_venues_route( db: Session = Depends(get_db), page_no: int = Query(1, ge=1, description="Page number"), limit: int = Query(20, ge=1, le=100, description="Records per page"), - action: Optional[str] = Query(None, description="Filter by action"), - location: Optional[str] = Query(None, description="Filter by location"), - avaialability: Optional[str] = Query(None, description="Filter by location"), ): try: return get_venues( db, page_no, limit, - action, - location, - avaialability ) except Exception as e: raise HTTPException( @@ -205,6 +202,55 @@ def update_venue_amenities( wheel_chair=payload.wheel_chair, av_equipements=payload.av_equipements, ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + +@router.get("/search") +def search_venues_route( + db: Session = Depends(get_db), + q: Optional[str] = Query(None, description="Search text"), + location: Optional[str] = Query(None), + min_price: Optional[int] = Query(None), + max_price: Optional[int] = Query(None), + wifi: bool | None = Query(None), + parking: bool | None = Query(None), + ac: bool | None = Query(None), + page_no: int = Query(1, ge=1), + limit: int = Query(20, ge=1, le=100), +): + return search_venues( + db=db, + q=q, + location=location, + min_price=min_price, + max_price=max_price, + wifi=wifi, + parking=parking, + ac=ac, + page_no=page_no, + limit=limit, + ) + +@router.post("/{venue_id}/availability") +def upload_venue_availability( + payload: VenueAvailabilityCreate, + venue_id: int, + db: Session = Depends(get_db), +): + try: + return add_venue_availability( + db=db, + venue_id=venue_id, + booking_types=payload.booking_types, + open_time=payload.open_time, + closing_time=payload.closing_time, + minimum_hours=payload.minimum_hours, + gap_between_bookings=payload.gap_between_bookings, + venue_price=payload.venue_price, + ) except Exception as e: raise HTTPException( status_code=400, diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py index 7d9a50ad2..c076fa9df 100644 --- a/BACKEND/app/main.py +++ b/BACKEND/app/main.py @@ -5,6 +5,7 @@ from app.model.venue import Venue from app.model.venue_amenities import VenueAmenities from app.model.venue_images import VenueImages +from app.model.venue_availability import VenueAvailability from app.api.user_routes import router as user_router from app.api.auth_routes import router as auth_router diff --git a/BACKEND/app/model/user.py b/BACKEND/app/model/user.py index 8ed17193a..6cc62f7c8 100644 --- a/BACKEND/app/model/user.py +++ b/BACKEND/app/model/user.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Integer, String, DateTime, func +from sqlalchemy import Column, Integer, String, DateTime, func, Boolean from sqlalchemy.orm import relationship from app.db.database import Base @@ -10,6 +10,7 @@ class User(Base): email = Column(String(255), unique=True, nullable=False) password = Column(String(255), nullable=False) role = Column(String(50), nullable=False, default="user") + is_active = Column(Boolean, default=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) updated_at = Column( diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py index a0261fa7f..0eccd41aa 100644 --- a/BACKEND/app/model/venue.py +++ b/BACKEND/app/model/venue.py @@ -26,8 +26,7 @@ class Venue(Base): venue_description = Column(Text) location = Column(String(255), nullable=False) capacity = Column(Integer, nullable=False) - venue_price = Column(Integer, nullable=False) - venue_availabilty = Column(String(255), nullable=False) # hourly or daily + is_available = Column(Boolean, nullable=False, default=False) not_available_reason = Column(Text, nullable=True, default="") is_approved = Column(Boolean, nullable=False, default=False) @@ -42,4 +41,5 @@ class Venue(Base): user = relationship("User", back_populates="venues") venue_amenities = relationship("VenueAmenities", back_populates="venue", cascade="all, delete-orphan") venue_images = relationship("VenueImages", back_populates="venue", cascade="all, delete-orphan") + venue_availability = relationship("VenueAvailability", back_populates="venue", cascade="all, delete-orphan") diff --git a/BACKEND/app/model/venue_availability.py b/BACKEND/app/model/venue_availability.py new file mode 100644 index 000000000..ccb928329 --- /dev/null +++ b/BACKEND/app/model/venue_availability.py @@ -0,0 +1,44 @@ +from sqlalchemy import ( + Column, + Integer, + String, + Boolean, + Text, + DateTime, + ForeignKey, + func +) +from sqlalchemy.orm import relationship +from app.db.database import Base + +class VenueAvailability(Base): + __tablename__ = "venue_availability" + + id = Column(Integer, primary_key=True, index=True) + venue_id = Column( + Integer, + ForeignKey("venues.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + booking_types = Column(String(255), nullable=False) # hourly or daily + + open_time = Column(String(100), nullable=False) + closing_time = Column(String(100), nullable=False) + + ### for hourly booking + minimum_hours = Column(Integer, nullable=True) + gap_between_bookings = Column(Integer, nullable=True) + + venue_price = Column(Integer, nullable=False) + + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now() + ) + + venue = relationship("Venue", back_populates="venue_availability") + diff --git a/BACKEND/app/schema/venue.py b/BACKEND/app/schema/venue.py index b8a1eb41a..b260c801f 100644 --- a/BACKEND/app/schema/venue.py +++ b/BACKEND/app/schema/venue.py @@ -42,3 +42,11 @@ class VenueAmenitiesUpdate(BaseModel): ac: bool = False wheel_chair: bool = False av_equipements: bool = False + +class VenueAvailabilityCreate(BaseModel): + booking_types: Literal["hourly", "daily"] + open_time: str + closing_time: str + minimum_hours: Optional[int] = None + gap_between_bookings: Optional[int] = None + venue_price: int \ No newline at end of file diff --git a/BACKEND/app/services/venue_service.py b/BACKEND/app/services/venue_service.py index 8eb953009..4cf6a7d44 100644 --- a/BACKEND/app/services/venue_service.py +++ b/BACKEND/app/services/venue_service.py @@ -4,28 +4,48 @@ from app.model.user import User from app.model.venue_amenities import VenueAmenities from app.model.venue_images import VenueImages +from app.model.venue_availability import VenueAvailability from typing import List +from sqlalchemy import or_ def get_venues( db: Session, page_no : int, limit : int, - action : str, - location : str, - avaialability : str ): try: offset = (page_no - 1) * limit venues = db.query(Venue).offset(offset).limit(limit).all() + + result = [] if not venues: return { "message": "venues are not added" } - - return venues + for venue in venues: + first_image = venue.venue_images[0] if venue.venue_images else None + price = venue.venue_availability[0].venue_price if venue.venue_availability else None + + result.append({ + "id": venue.id, + "user_id": venue.user_id, + "venue_name": venue.venue_name, + "venue_description": venue.venue_description, + "location": venue.location, + "capacity": venue.capacity, + "is_available": venue.is_available, + "is_approved": venue.is_approved, + "not_available_reason": venue.not_available_reason, + "created_at": venue.created_at, + "updated_at": venue.updated_at, + "image": first_image.image_url if first_image else None, + "price": price + }) + + return result except Exception as e: raise Exception(f"Error occurred while fetching venues: {str(e)}") @@ -46,8 +66,21 @@ def get_venue_details_by_id( "message": "venues details is not present" } - return venue - + return { + "id": venue.id, + "venue_name": venue.venue_name, + "location": venue.location, + "venue_description": venue.venue_description, + "capacity": venue.capacity, + "not_available_reason": venue.not_available_reason, + "is_approved": venue.is_approved, + "venue_price": venue.venue_price, + "is_available": venue.is_available, + "created_at": venue.created_at, + "updated_at": venue.updated_at, + "amenities": venue.venue_amenities, + "images": venue.venue_images + } def add_venue( db: Session, @@ -56,8 +89,6 @@ def add_venue( venue_description: str, location: str, capacity: int, - venue_price: int, - venue_availabilty: str, ): user = ( @@ -75,8 +106,6 @@ def add_venue( venue_description=venue_description, location=location, capacity=capacity, - venue_price=venue_price, - venue_availabilty=venue_availabilty, ) @@ -295,4 +324,113 @@ def edit_venue_amenities( return { "message": "Amenities updated successfully" + } + +def search_venues( + db: Session, + q: str | None, + location: str | None, + min_price: int | None, + max_price: int | None, + wifi: bool | None, + parking: bool | None, + ac: bool | None, + page_no: int, + limit: int, +): + offset = (page_no - 1) * limit + + query = db.query(Venue) + + if q: + search_text = f"%{q}%" + query = query.filter( + or_( + Venue.venue_name.ilike(search_text), + Venue.location.ilike(search_text), + Venue.venue_description.ilike(search_text), + ) + ) + + if location: + query = query.filter(Venue.location.ilike(f"%{location}%")) + + query = query.join(VenueAvailability) + + if min_price is not None: + query = query.filter(VenueAvailability.venue_price >= min_price) + + if max_price is not None: + query = query.filter(VenueAvailability.venue_price <= max_price) + + query = query.join(VenueAmenities) + + if wifi is not None: + query = query.filter(VenueAmenities.wifi == wifi) + + if parking is not None: + query = query.filter(VenueAmenities.parking == parking) + + if ac is not None: + query = query.filter(VenueAmenities.ac == ac) + + venues = query.offset(offset).limit(limit).all() + + result = [] + + if not venues: + return { + "message": "venues are not added" + } + + for venue in venues: + first_image = venue.venue_images[0] if venue.venue_images else None + price = venue.venue_availability[0].venue_price if venue.venue_availability else None + + result.append({ + "id": venue.id, + "user_id": venue.user_id, + "venue_name": venue.venue_name, + "venue_description": venue.venue_description, + "location": venue.location, + "capacity": venue.capacity, + "is_available": venue.is_available, + "is_approved": venue.is_approved, + "not_available_reason": venue.not_available_reason, + "created_at": venue.created_at, + "updated_at": venue.updated_at, + "image": first_image.image_url if first_image else None, + "price": price + }) + + return result + + +def add_venue_availability( + db: Session, + venue_id: int, + booking_types: str, + open_time: str, + closing_time: str, + minimum_hours: int | None, + gap_between_bookings: int | None, + venue_price: int, +): + new_availability = VenueAvailability( + venue_id=venue_id, + booking_types=booking_types, + open_time=open_time, + closing_time=closing_time, + minimum_hours=minimum_hours, + gap_between_bookings=gap_between_bookings, + venue_price=venue_price, + ) + + db.add(new_availability) + db.commit() + db.refresh(new_availability) + + return { + "message": "Venue availability added successfully", + "availability_id": new_availability.id } \ No newline at end of file From 13d9133885afdb73a454d8f1e9b180336e7e673b Mon Sep 17 00:00:00 2001 From: nafanpe Date: Thu, 18 Jun 2026 18:44:57 +0530 Subject: [PATCH 23/95] feat: Add Host Dashboard --- FRONTEND/src/App.jsx | 4 +- FRONTEND/src/components/FormBooking.jsx | 42 +--- FRONTEND/src/pages/LoginPage.jsx | 2 +- FRONTEND/src/pages/OwnerDashboard.jsx | 279 ++++++++++++++++++++++++ 4 files changed, 294 insertions(+), 33 deletions(-) create mode 100644 FRONTEND/src/pages/OwnerDashboard.jsx diff --git a/FRONTEND/src/App.jsx b/FRONTEND/src/App.jsx index 0ebad17e1..89e03f054 100644 --- a/FRONTEND/src/App.jsx +++ b/FRONTEND/src/App.jsx @@ -3,6 +3,7 @@ import HomePage from "./pages/HomePage" import LoginPage from "./pages/LoginPage" import SpaceListing from "./pages/VenueListing" import BookMyVenueLanding from "./pages/HostLandingPage" +import OwnerDashboard from './pages/OwnerDashboard'; function App() { @@ -14,7 +15,8 @@ function App() { } /> } /> } /> - } /> + } /> + } />
diff --git a/FRONTEND/src/components/FormBooking.jsx b/FRONTEND/src/components/FormBooking.jsx index 4ee5e98f5..c22f5a2ba 100644 --- a/FRONTEND/src/components/FormBooking.jsx +++ b/FRONTEND/src/components/FormBooking.jsx @@ -5,7 +5,7 @@ import 'react-calendar/dist/Calendar.css'; export default function FormBooking ({venue}) { const [selectedDate, setSelectedDate] = useState(new Date()) - const [selectedSession, setSelectedSession] = useState("") + const [selectedSession, setSelectedSession] = useState("Hourly") const handleChange = (e) => { setSelectedSession(e.target.value) @@ -14,14 +14,7 @@ export default function FormBooking ({venue}) { return (
-
- -
+ {/* 2. Unified Session & Time Slot Card */}
@@ -36,7 +29,6 @@ export default function FormBooking ({venue}) { className="w-full pt-6 pb-2 px-3 text-sm font-medium text-gray-800 bg-transparent outline-none appearance-none cursor-pointer" onChange={handleChange} > - @@ -74,27 +66,15 @@ export default function FormBooking ({venue}) { )}
)} - {selectedSession === "Daily" && ( -
- {venue.availdays && venue.availdays.length > 0 ? ( -
- {venue.availdays.map((day) => ( - - ))} -
- ) : ( -
- No specific daily slots listed for this venue. -
- )} -
- )} + +
+
+
) diff --git a/FRONTEND/src/pages/LoginPage.jsx b/FRONTEND/src/pages/LoginPage.jsx index e73e94638..fe57fe7a5 100644 --- a/FRONTEND/src/pages/LoginPage.jsx +++ b/FRONTEND/src/pages/LoginPage.jsx @@ -115,7 +115,7 @@ export default function LoginPage() { {/* Success Message */} {successMessage && ( -
+
{successMessage}
)} diff --git a/FRONTEND/src/pages/OwnerDashboard.jsx b/FRONTEND/src/pages/OwnerDashboard.jsx new file mode 100644 index 000000000..587ebb61c --- /dev/null +++ b/FRONTEND/src/pages/OwnerDashboard.jsx @@ -0,0 +1,279 @@ +import React, { useState } from 'react'; +import { + Layout, Building, Rupee, Cog, PlusCircle, Bell, Message, + Clock1, MapPin, DotsVertical, Star, Menu, X +} from '@mynaui/icons-react'; + +// ========================================== +// 1. MOCK DATA (Move to a separate data.js file later) +// ========================================== +const RECENT_BOOKINGS = [ + { id: 1, guestName: 'Sarah Jenkins', guestAvatar: 'https://i.pravatar.cc/150?u=sarah', status: 'PENDING', venueName: 'Maple Community Hall', venueImage: 'https://images.unsplash.com/photo-1505843513577-22bb7d21e455?auto=format&fit=crop&w=150&q=80', date: 'Oct 24, 14:00 - 18:00', price: '$120.00' }, + { id: 2, guestName: 'Alex Miller', guestAvatar: 'https://i.pravatar.cc/150?u=alex', status: 'CONFIRMED', venueName: 'Skyline Rooftop Garden', venueImage: 'https://images.unsplash.com/photo-1573164713988-8665fc963095?auto=format&fit=crop&w=150&q=80', date: 'Oct 26, 18:00 - 22:00', price: '$300.00' } +]; + +const LISTED_PROPERTIES = [ + { id: 1, name: 'Maple Hall', price: '$45/hr', rating: '4.9', image: 'https://images.unsplash.com/photo-1505843513577-22bb7d21e455?auto=format&fit=crop&w=400&q=80', isActive: true }, + { id: 2, name: 'Skyline Rooftop', price: '$75/hr', rating: '4.7', image: 'https://images.unsplash.com/photo-1573164713988-8665fc963095?auto=format&fit=crop&w=400&q=80', isActive: true }, + { id: 3, name: 'Art Studio', price: '$35/hr', rating: '5.0', image: 'https://images.unsplash.com/photo-1600607688969-a5bfcd646154?auto=format&fit=crop&w=400&q=80', isActive: false } +]; + +// ========================================== +// 2. REUSABLE UI COMPONENTS +// ========================================== +const SidebarItem = ({ icon: Icon, label, isActive, onClick }) => ( + +); + +const ToggleSwitch = ({ isActive }) => ( +
+
+
+); + +// ========================================== +// 3. LAYOUT COMPONENTS +// ========================================== + +const Sidebar = ({ activeTab, setActiveTab, isOpen, setIsOpen }) => ( + <> + {/* Mobile Overlay Background */} + {isOpen && ( +
setIsOpen(false)} + /> + )} + + {/* Sidebar Container */} + + +); + +const TopHeader = () => ( +
+
+

Welcome back, Community Host

+

High-efficiency overview of your venue network.

+
+
+); + +const StatsOverview = () => ( +
+
+
+ +
+
+
+ 12 + Pending Requests +
+
+
+
+
+ +
+
+
+ 28k + Total Revenue +
+
+
+
+
+ +
+
+
+ 5 + Active Venues +
+
+
+
+); + +const RecentBookings = () => ( +
+
+

Recent Bookings

+
+ +
+ {RECENT_BOOKINGS.map(booking => ( +
+
+ {booking.guestName} +
+

{booking.guestName}

+ + {booking.status} + +
+
+
+ Venue +
+

{booking.venueName}

+

{booking.date}

+
+
+
+

{booking.price}

+ {booking.status === 'PENDING' ? ( +
+ + +
+ ) : ( + + )} +
+
+ ))} +
+
+); + +const ListedProperties = () => ( + <> +
+

Listed Properties

+
+ +
+ {LISTED_PROPERTIES.map(venue => ( +
+
+ {venue.name} +
+ + {venue.rating} +
+
+
+
+

{venue.name}

+
+
+

{venue.price}

+ +
+
+
+ ))} + + +
+ +); + +// ========================================== +// 4. MAIN PARENT COMPONENT +// ========================================== + +export default function OwnerDashboard() { + const [activeTab, setActiveTab] = useState('Dashboard'); + const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false); + + return ( +
+ + {/* Extracted Sidebar Component */} + + + {/* MAIN CONTENT AREA */} +
+ + {/* Mobile Navbar Header (Only visible on small screens) */} +
+

BookMyVenue

+ +
+ + {/* Dashboard Content Container */} +
+ + {/* Stacking the extracted components sequentially */} + + + + + + + +
+
+ +
+ ); +} \ No newline at end of file From f1947917bfa475760133b1b5751222ca5d9ea6ba Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Thu, 18 Jun 2026 23:06:58 +0530 Subject: [PATCH 24/95] venue isactive and is_avialable added in get APIs --- BACKEND/app/api/admin_routes.py | 2 +- BACKEND/app/main.py | 3 ++- BACKEND/app/services/venue_service.py | 23 ++++++++++++++++------- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/BACKEND/app/api/admin_routes.py b/BACKEND/app/api/admin_routes.py index b23b970fe..990d9e903 100644 --- a/BACKEND/app/api/admin_routes.py +++ b/BACKEND/app/api/admin_routes.py @@ -18,7 +18,7 @@ router = APIRouter( prefix="/admin", - tags=["Venues"] + tags=["Admin"] ) @router.post("/venue/{venue_id}") diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py index c076fa9df..d82fd83eb 100644 --- a/BACKEND/app/main.py +++ b/BACKEND/app/main.py @@ -10,6 +10,7 @@ from app.api.user_routes import router as user_router from app.api.auth_routes import router as auth_router from app.api.venue_routes import router as venue_router +from app.api.admin_routes import router as admin_router from fastapi.middleware.cors import CORSMiddleware @@ -35,7 +36,7 @@ async def startup(): app.include_router(user_router) app.include_router(auth_router) app.include_router(venue_router) - +app.include_router(admin_router) @app.get("/") def read_root(): diff --git a/BACKEND/app/services/venue_service.py b/BACKEND/app/services/venue_service.py index 4cf6a7d44..8ca0f3a1e 100644 --- a/BACKEND/app/services/venue_service.py +++ b/BACKEND/app/services/venue_service.py @@ -16,7 +16,10 @@ def get_venues( try: offset = (page_no - 1) * limit - venues = db.query(Venue).offset(offset).limit(limit).all() + venues = db.query(Venue).filter( + Venue.is_available.is_(True), + Venue.is_approved.is_(True) + ).offset(offset).limit(limit).all() result = [] @@ -66,6 +69,8 @@ def get_venue_details_by_id( "message": "venues details is not present" } + venue_price = venue.venue_availability[0].venue_price if venue.venue_availability else None + return { "id": venue.id, "venue_name": venue.venue_name, @@ -74,12 +79,13 @@ def get_venue_details_by_id( "capacity": venue.capacity, "not_available_reason": venue.not_available_reason, "is_approved": venue.is_approved, - "venue_price": venue.venue_price, + "venue_price": venue_price, "is_available": venue.is_available, "created_at": venue.created_at, "updated_at": venue.updated_at, "amenities": venue.venue_amenities, - "images": venue.venue_images + "images": venue.venue_images, + "availability": venue.venue_availability } def add_venue( @@ -221,10 +227,10 @@ def update_venue_active_status( raise Exception("Invalid venue") if status.lower() == "active": - venue.is_active = True + venue.is_available = True elif status.lower() == "inactive": - venue.is_active = False + venue.is_available = False venue.not_available_reason = reason else: @@ -236,7 +242,7 @@ def update_venue_active_status( return { "message": f"Venue {status.lower()} successfully", "venue_id": venue.id, - "is_active": venue.is_active + "is_available": venue.is_available } @@ -374,7 +380,10 @@ def search_venues( if ac is not None: query = query.filter(VenueAmenities.ac == ac) - venues = query.offset(offset).limit(limit).all() + venues = db.query(Venue).filter( + Venue.is_available.is_(True), + Venue.is_approved.is_(True) + ).offset(offset).limit(limit).all() result = [] From 8df1912eead189551e59db8b986d0e7f4ce76ba6 Mon Sep 17 00:00:00 2001 From: nafanpe Date: Fri, 19 Jun 2026 16:00:38 +0530 Subject: [PATCH 25/95] Host Dashbaord fixes --- FRONTEND/src/pages/VenueListing.jsx | 40 +++++++++++++++++++++++++---- FRONTEND/src/services/apiService.js | 5 ++++ 2 files changed, 40 insertions(+), 5 deletions(-) diff --git a/FRONTEND/src/pages/VenueListing.jsx b/FRONTEND/src/pages/VenueListing.jsx index 422a24a66..0f7e872a7 100644 --- a/FRONTEND/src/pages/VenueListing.jsx +++ b/FRONTEND/src/pages/VenueListing.jsx @@ -5,23 +5,53 @@ import { Wifi, Parking, Monitor, - Coffee + Coffee, + SpinnerOne } from '@mynaui/icons-react'; -import { useParams } from 'react-router-dom'; import { VENUE_DATA } from '../data/VenueCardData'; import FormBooking from "../components/FormBooking" +import apiService from '../services/apiService'; +import { useParams } from 'react-router-dom'; +import { useEffect, useState } from 'react'; + export default function SpaceListing() { - const {id} = useParams(); - const venue = VENUE_DATA.find((v) => String(v.id) === id) + const [venue, setVenue] = useState(); + const [isLoading, setIsLoading] = useState(true); - const venue_name = venue.title + const {id} = useParams() + + useEffect(() => { + const FetchVenue = async() => { + try { + const response = await apiService.getVenueByID(id) + console.log(response.data) + setVenue(response.data) + } catch (error) { + console.error(error) + } finally { + setIsLoading(false) + } + } + FetchVenue(); + }, [id]) + + const venue_name = venue.venue_name const venue_details = venue.details const venue_price = venue.price const venue_rating = venue.rating const venue_capacity = venue.capacity + if(isLoading){ + return ( +
+ +

Loading, Please Wait!

+
+ ) + } + if (!venue) { return
Venue not found!
; } diff --git a/FRONTEND/src/services/apiService.js b/FRONTEND/src/services/apiService.js index 62688aa80..37c166c5f 100644 --- a/FRONTEND/src/services/apiService.js +++ b/FRONTEND/src/services/apiService.js @@ -1,3 +1,4 @@ +import { AsteriskWaves } from "@mynaui/icons-react"; import axiosInstance from "./axiosInstance"; const apiService = { @@ -12,6 +13,10 @@ const apiService = { getAllVenues: async () => { const response = await axiosInstance.get('/venues/') return response.data + }, + getVenueByID: async (id) => { + const response = await axiosInstance.get(`/venues/details/${id}`) + return response.data } } From 1fab81165e79033887e9cceb0919920aa0e2e5ef Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Sat, 20 Jun 2026 00:36:08 +0530 Subject: [PATCH 26/95] payment, booking schema added, razorpay apis --- BACKEND/app/api/payment_routes.py | 56 ++++++++++++++++++++++ BACKEND/app/core/config.py | 2 + BACKEND/app/main.py | 2 + BACKEND/app/model/bookings.py | 59 ++++++++++++++++++++++++ BACKEND/app/model/orders.py | 39 ++++++++++++++++ BACKEND/app/model/user.py | 4 +- BACKEND/app/model/venue.py | 2 + BACKEND/app/schema/payment.py | 10 ++++ BACKEND/app/services/booking_service.py | 0 BACKEND/app/services/order_service.py | 0 BACKEND/requirements.txt | Bin 0 -> 2336 bytes 11 files changed, 173 insertions(+), 1 deletion(-) create mode 100644 BACKEND/app/api/payment_routes.py create mode 100644 BACKEND/app/model/bookings.py create mode 100644 BACKEND/app/model/orders.py create mode 100644 BACKEND/app/schema/payment.py create mode 100644 BACKEND/app/services/booking_service.py create mode 100644 BACKEND/app/services/order_service.py create mode 100644 BACKEND/requirements.txt diff --git a/BACKEND/app/api/payment_routes.py b/BACKEND/app/api/payment_routes.py new file mode 100644 index 000000000..4ea1fca3c --- /dev/null +++ b/BACKEND/app/api/payment_routes.py @@ -0,0 +1,56 @@ +import os +import razorpay +from fastapi import APIRouter, HTTPException +from app.core.config import settings + +router = APIRouter(prefix="/payments", tags=["Payments"]) + +client = razorpay.Client( + auth=( + settings.RAZORPAY_KEY_ID, + settings.RAZORPAY_KEY_SECRET + ) +) + +@router.post("/create-order") +def create_order(payload: CreateOrderRequest): + try: + order_data = { + "amount": payload.amount * 100, # ₹500 => 50000 paise + "currency": "INR", + "receipt": "receipt_order_001", + "payment_capture": 1 + } + + order = client.order.create(data=order_data) + + return { + "order_id": order["id"], + "amount": order["amount"], + "currency": order["currency"], + "key": settings.RAZORPAY_KEY_ID + } + + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + + +@router.post("/verify-payment") +def verify_payment(payload: VerifyPaymentRequest): + try: + client.utility.verify_payment_signature({ + "razorpay_order_id": payload.razorpay_order_id, + "razorpay_payment_id": payload.razorpay_payment_id, + "razorpay_signature": payload.razorpay_signature + }) + + return { + "success": True, + "message": "Payment verified successfully" + } + + except Exception: + raise HTTPException( + status_code=400, + detail="Payment verification failed" + ) \ No newline at end of file diff --git a/BACKEND/app/core/config.py b/BACKEND/app/core/config.py index 0bcda02c5..7bb6c2a5e 100644 --- a/BACKEND/app/core/config.py +++ b/BACKEND/app/core/config.py @@ -9,6 +9,8 @@ class Settings(BaseSettings): CLOUDINARY_API_KEY: str CLOUDINARY_API_SECRET: str CLOUDINARY_FOLDER_NAME: str + RAZORPAY_KEY_ID: str + RAZORPAY_KEY_SECRET: str class Config: env_file = ".env" diff --git a/BACKEND/app/main.py b/BACKEND/app/main.py index d82fd83eb..348af18f1 100644 --- a/BACKEND/app/main.py +++ b/BACKEND/app/main.py @@ -6,6 +6,8 @@ from app.model.venue_amenities import VenueAmenities from app.model.venue_images import VenueImages from app.model.venue_availability import VenueAvailability +from app.model.orders import Order +from app.model.bookings import Booking from app.api.user_routes import router as user_router from app.api.auth_routes import router as auth_router diff --git a/BACKEND/app/model/bookings.py b/BACKEND/app/model/bookings.py new file mode 100644 index 000000000..47499bc2b --- /dev/null +++ b/BACKEND/app/model/bookings.py @@ -0,0 +1,59 @@ +from sqlalchemy import ( + Column, + Integer, + String, + Date, + DateTime, + ForeignKey, + func +) +from sqlalchemy.orm import relationship +from app.db.database import Base + + +class Booking(Base): + __tablename__ = "bookings" + + id = Column(Integer, primary_key=True, index=True) + + user_id = Column( + Integer, + ForeignKey("users.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + venue_id = Column( + Integer, + ForeignKey("venues.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + order_id = Column( + Integer, + ForeignKey("orders.id", ondelete="CASCADE"), + nullable=False, + index=True + ) + + booking_date = Column(Date, nullable=False) + booking_time = Column(String(100), nullable=True) + + status = Column(String(50), nullable=False, default="created") + # status can be "created", "confirmed", "cancelled", "completed" + + created_at = Column( + DateTime(timezone=True), + server_default=func.now() + ) + + updated_at = Column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now() + ) + + user = relationship("User", back_populates="bookings") + venue = relationship("Venue", back_populates="bookings") + order = relationship("Order", back_populates="booking") \ No newline at end of file diff --git a/BACKEND/app/model/orders.py b/BACKEND/app/model/orders.py new file mode 100644 index 000000000..ce46f4c7d --- /dev/null +++ b/BACKEND/app/model/orders.py @@ -0,0 +1,39 @@ +from sqlalchemy import Column, Integer, String, DateTime, func, Boolean, ForeignKey +from sqlalchemy.orm import relationship +from app.db.database import Base +from typing import Literal + + +class Order(Base): + __tablename__ = "orders" + + id = Column(Integer, primary_key=True, index=True) + + user_id = Column(Integer, ForeignKey("users.id"), nullable=False) + venue_id = Column(Integer, ForeignKey("venues.id"), nullable=False) + + razorpay_order_id = Column(String(255), unique=True, nullable=False) + razorpay_payment_id = Column(String(255), nullable=True) + + amount = Column(Integer, nullable=False) # store paise, not rupees + currency = Column(String(10), nullable=False, default="INR") + + status = Column( + String(50), + nullable=False, + default="pending" + ) + # status can be "pending", "paid", "failed", "refunded" + + payment_time = Column(DateTime(timezone=True), nullable=True) + + created_at = Column(DateTime(timezone=True), server_default=func.now()) + updated_at = Column( + DateTime(timezone=True), + server_default=func.now(), + onupdate=func.now() + ) + + venue = relationship("Venue", back_populates="orders") + user = relationship("User", back_populates="orders") + booking = relationship("Booking", back_populates="order", uselist=False) diff --git a/BACKEND/app/model/user.py b/BACKEND/app/model/user.py index 6cc62f7c8..71bcbb094 100644 --- a/BACKEND/app/model/user.py +++ b/BACKEND/app/model/user.py @@ -19,4 +19,6 @@ class User(Base): onupdate=func.now() ) - venues = relationship("Venue", back_populates="user", cascade="all, delete-orphan") \ No newline at end of file + venues = relationship("Venue", back_populates="user", cascade="all, delete-orphan") + orders = relationship("Order", back_populates="user", cascade="all, delete-orphan") + bookings = relationship("Booking", back_populates="user", cascade="all, delete-orphan") \ No newline at end of file diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py index 0eccd41aa..423af5652 100644 --- a/BACKEND/app/model/venue.py +++ b/BACKEND/app/model/venue.py @@ -42,4 +42,6 @@ class Venue(Base): venue_amenities = relationship("VenueAmenities", back_populates="venue", cascade="all, delete-orphan") venue_images = relationship("VenueImages", back_populates="venue", cascade="all, delete-orphan") venue_availability = relationship("VenueAvailability", back_populates="venue", cascade="all, delete-orphan") + orders = relationship("Order", back_populates="venue", cascade="all, delete-orphan") + bookings = relationship("Booking", back_populates="venue", cascade="all, delete-orphan") diff --git a/BACKEND/app/schema/payment.py b/BACKEND/app/schema/payment.py new file mode 100644 index 000000000..72624a5e1 --- /dev/null +++ b/BACKEND/app/schema/payment.py @@ -0,0 +1,10 @@ +from pydantic import BaseModel + +class CreateOrderRequest(BaseModel): + amount: int # amount in rupees + + +class VerifyPaymentRequest(BaseModel): + razorpay_order_id: str + razorpay_payment_id: str + razorpay_signature: str \ No newline at end of file diff --git a/BACKEND/app/services/booking_service.py b/BACKEND/app/services/booking_service.py new file mode 100644 index 000000000..e69de29bb diff --git a/BACKEND/app/services/order_service.py b/BACKEND/app/services/order_service.py new file mode 100644 index 000000000..e69de29bb diff --git a/BACKEND/requirements.txt b/BACKEND/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..6f3958413fc392f262181187f47e2e5aa70d87e7 GIT binary patch literal 2336 zcmZ{m%}yIZ5QOI(DUV_+UK>abIdF}Fl!#MK$TkKW|7N`on1@eNU)PRjjS^W4i@T@0 zy1KfDKflLmm1b$3CTWqT>9anA)YtP-KS$|(dY8s&qt7-qdTZo+pL%+>t@j7{@%-Pn zNyoI)xqU>(_bSfh*hgjFhHa$p2Wfb+PL!jL)6L1w`9a{CDrPN2i}auexVSE7fukKMO@8 z_A$KRbQCeFHpW&twz7{SA6d*|U-*qWSH4Y;;kQfkHrgnq6#wa^+px+ zwhjd5t(?GTr+AH`Zgs-^_1nlGAl|*a#+~z@l+zWUnXz)Ml<`;OTF4uPP*sjrdifxm zQSEdp*GsqQ*Dz{6_vtA=OlMyzs9K%<%hSBSJ!K`Er*f*x3B9o$o@e@Of;Vf93Q&_Z zH`njM3DF?1`|MnmH)3=e_0Ph`k^AXWRF^$qi0+qpKttzIUQRix=!mn!FsgzPaFL%* zeTkkw$x?M}SxGngeyNO{0+CO;i}bLTm3e+vY}A8>PzL^}zB-{J65a>dZ}t8lt}+(7 z<1Vfh?I6EZ*s|`NZ>Ljxedy~X>>y$vcz^6yPR6cpi-S?tO?5K95s`LIMgSSw|bneqr)i3gAc6<8W-EKZhP43v+_&p(I*N|u>>MB020*dx#Q;WG}1e( z5(Mx?FWBI8{Jy;_sO7TLmAz2T#2V9p8n}(tr;dQ!9p20CjbLS!`|~z^7n9eDK*j9j zbJi}}UU~B}lU85ZPegRJS;=KL`YyF9Y&Fh&$8f82=Wu`f27w>%40!0Cxsxwb12fda z39H|_FS_#X@P-%$8}L?YK1j86n67lQmM!`$9-Cli7fmlv($Um IQ+KQX1%r4~5&!@I literal 0 HcmV?d00001 From b102e0f89117188f435e74e7f91e64dfed55337a Mon Sep 17 00:00:00 2001 From: nafanpe Date: Sat, 20 Jun 2026 00:42:30 +0530 Subject: [PATCH 27/95] save current progess before pulling backend changes --- FRONTEND/src/components/FormBooking.jsx | 193 ++++++++++++------- FRONTEND/src/hooks/useVenueGrid.jsx | 9 +- FRONTEND/src/pages/VenueListing.jsx | 241 ++++++++++++++++-------- FRONTEND/src/services/apiService.js | 3 +- 4 files changed, 299 insertions(+), 147 deletions(-) diff --git a/FRONTEND/src/components/FormBooking.jsx b/FRONTEND/src/components/FormBooking.jsx index c22f5a2ba..eff37b0c6 100644 --- a/FRONTEND/src/components/FormBooking.jsx +++ b/FRONTEND/src/components/FormBooking.jsx @@ -2,80 +2,143 @@ import { useState } from 'react'; import Calendar from 'react-calendar'; import 'react-calendar/dist/Calendar.css'; -export default function FormBooking ({venue}) { +export default function FormBooking ({ + venue, + selectedDate, + setSelectedDate, + selectedSession, + setSelectedSession, + selectedTimes, + setSelectedTimes +}) { - const [selectedDate, setSelectedDate] = useState(new Date()) - const [selectedSession, setSelectedSession] = useState("Hourly") + // handles muiltiple time interval selection and removing. + const handleTimeClick = (time) => { + setSelectedTimes((prevTimes) => { + if(prevTimes.includes(time)){ + return prevTimes.filter((t) => t !== time) + } else { + return [...prevTimes, time] + } + }) + } const handleChange = (e) => { setSelectedSession(e.target.value) } - - return ( -
- + + // converts string time(9am) to (9, 0, 0, 0) format + const SetTimeFromString = (dateObject, StringTime) => { + const [time, modifier] = StringTime.split(" "); + let [hour, minute] = time.split(":").map(Number) + + if(modifier === "PM" && hour < 12) hour += 12 + if(modifier === "AM" && hour === 12) hour = 0 + + dateObject.setHours(hour, minute, 0, 0); + return dateObject + } + + // generate time interval list to display time intervals (9am - 11am) + const GenerateHourlyTimeIntervals = () => { + let intervals = [] + const startTime = new Date() + + const endTime = new Date() + + SetTimeFromString(startTime, venue.availability[0].open_time) + SetTimeFromString(endTime, venue.availability[0].closing_time) + while(startTime < endTime){ + const startStr = startTime.toLocaleTimeString('en-US', {hour: 'numeric', minute: '2-digit', hour12: true}) - {/* 2. Unified Session & Time Slot Card */} -
+ startTime.setHours(startTime.getHours() + 2); - {/* Select Wrapper */} -
-
- Session -
- - - - {/* FIX: SVG is now positioned specifically inside the select area */} - - - -
+ const endStr = startTime.toLocaleTimeString('en-US', {hour: 'numeric', minute: '2-digit', hour12: true}) - {/* Hourly Slots - Now seamlessly attached to the bottom of the select */} - {selectedSession === "Hourly" && ( -
- {venue.availtime && venue.availtime.length > 0 ? ( -
- {venue.availtime.map((time) => ( - - ))} -
- ) : ( -
- No hourly slots available for this venue. -
- )} -
- )} - -
-
- + intervals.push(`${startStr} - ${endStr}`) + } + intervals.pop() + + return(intervals); + } + const availTime = GenerateHourlyTimeIntervals(); + + const isHourly = venue.availability.some(avail => avail.booking_types == "hourly") + const isDaily = venue.availability.some(avail => avail.booking_types == "daily") + + return ( + <> +
+ {/* 2. Unified Session & Time Slot Card */} +
+ + {/* Select Wrapper */} +
+
+ Session
+ + + + {/* FIX: SVG is now positioned specifically inside the select area */} + + +
- ) + + {/* hourly Slots - Now seamlessly attached to the bottom of the select */} + {selectedSession === "hourly" && ( +
+ {availTime && availTime.length > 0 ? ( +
+ {availTime.map((time) => { + const isSelected = selectedTimes.includes(time) + return ( + + ) + })} +
+ ) : ( +
+ No hourly slots available for this venue. +
+ )} +
+ )} + +
+
+ +
+
+ + ) } \ No newline at end of file diff --git a/FRONTEND/src/hooks/useVenueGrid.jsx b/FRONTEND/src/hooks/useVenueGrid.jsx index 2dcd69a5c..0aaf44d6a 100644 --- a/FRONTEND/src/hooks/useVenueGrid.jsx +++ b/FRONTEND/src/hooks/useVenueGrid.jsx @@ -13,8 +13,13 @@ export const useVenueGrid = () => { setVenues(data) } catch(err) { - console.error("Something Went Wrong!", err) - setError(err) + console.error("Something Went Wrong!", err.response?.data) + const errorMessage = + err.response?.data?.detail || + err.response?.data?.message || + "Failed to load spaces."; + + setError(new Error(errorMessage)); } finally { setIsLoading(false) diff --git a/FRONTEND/src/pages/VenueListing.jsx b/FRONTEND/src/pages/VenueListing.jsx index 0f7e872a7..23e157cbe 100644 --- a/FRONTEND/src/pages/VenueListing.jsx +++ b/FRONTEND/src/pages/VenueListing.jsx @@ -4,7 +4,7 @@ import { StarSolid, Wifi, Parking, - Monitor, + Monitor, Coffee, SpinnerOne } from '@mynaui/icons-react'; @@ -12,36 +12,92 @@ import { VENUE_DATA } from '../data/VenueCardData'; import FormBooking from "../components/FormBooking" import apiService from '../services/apiService'; import { useParams } from 'react-router-dom'; -import { useEffect, useState } from 'react'; +import { isValidElement, useEffect, useState } from 'react'; +const ImageGalleryModal = ({ isOpen, onClose, images }) => { + useEffect(() => { + if (isOpen) { + document.body.style.overflow = 'hidden'; + } else { + document.body.style.overflow = 'unset'; + } + return () => { + document.body.style.overflow = 'unset'; + }; + }, [isOpen]); + + if (!isOpen) return null; + + return ( +
+ + {/* Header (Sticky so it stays visible while scrolling) */} +
+

Photo Tour

+ +
+ + {/* Gallery Grid */} +
+
+ {images?.map((img, index) => ( +
+ {`Venue +
+ ))} +
+
+ +
+ ); +} export default function SpaceListing() { - const [venue, setVenue] = useState(); + const [selectedDate, setSelectedDate] = useState(new Date()); + const [selectedSession, setSelectedSession] = useState("hourly"); + const [selectedTimes, setSelectedTimes] = useState([]); + const [isGalleryOpen, SetIsGalleryOpen] = useState(false); + + const [venue, setVenue] = useState(null); const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + + const {id} = useParams() - const {id} = useParams() - useEffect(() => { const FetchVenue = async() => { try { const response = await apiService.getVenueByID(id) - console.log(response.data) - setVenue(response.data) + console.log(response) + setVenue(response) + } catch (error) { - console.error(error) + console.error("Failed to Fetch data: ",error) + const errorMessage = err.response?.data?.detail || err.response?.data?.message || "Failed to load venue details."; + setError(errorMessage) + } finally { setIsLoading(false) } } - FetchVenue(); - }, [id]) - - const venue_name = venue.venue_name - const venue_details = venue.details - const venue_price = venue.price - const venue_rating = venue.rating - const venue_capacity = venue.capacity + if(id){ + FetchVenue(); + } + }, [id]); + if(isLoading){ return ( @@ -52,68 +108,85 @@ export default function SpaceListing() { ) } - if (!venue) { + if (error || !venue) { return
Venue not found!
; } + // get all images from venues object + const imageLinks = venue.images?.map(i => i.image_url) || []; + + // Variables + const venue_name = venue.venue_name + const venue_details = venue.venue_description + const venue_price = venue.venue_price + const venue_rating = venue.rating + const venue_capacity = venue.capacity + const venue_location= venue.location + return (
- -
- -
- {/* --- Image Gallery Section --- */} -
-
- Main Loft Space setIsGalleryOpen(false)} + images={imageLinks} /> -
- - {/* Right 4-Grid Images */} -
- - {/* Top Left */} -
- Lounge Area -
- {/* Top Right */} -
- Kitchen -
+ {/* --- Image Gallery Section --- */} +
+
+ Main Loft Space +
+ + {/* Right 4-Grid Images */} +
+ {/* Top Left */} +
+ Lounge Area +
- {/* Bottom Left */} -
- Seating Setup -
+ {/* Top Right */} +
+ Kitchen +
- {/* Bottom Right */} -
- Patio -
+ {/* Bottom Left */} +
+ Seating Setup +
+ {/* Bottom Right */} +
+ Patio +
+
+
-
{/* Main Content Split */}
@@ -123,17 +196,17 @@ export default function SpaceListing() { {/* Header Info */}
-
+ {/*
Ideal for Weddings Parties Workshops -
+
*/}

{venue_name}

- Downtown Arts District, Portland + {venue_location}
@@ -141,12 +214,11 @@ export default function SpaceListing() { {venue_capacity}
+ {venue_rating &&
· -
{venue_rating} - (128 reviews) -
+
}
@@ -192,12 +264,20 @@ export default function SpaceListing() { {/* Price */}
- {venue_price} - / session + {venue_price + "₹"} + / hour
{/* Form Inputs */} - + {/* Submit Button */}
diff --git a/FRONTEND/src/services/apiService.js b/FRONTEND/src/services/apiService.js index 37c166c5f..547bf896f 100644 --- a/FRONTEND/src/services/apiService.js +++ b/FRONTEND/src/services/apiService.js @@ -17,7 +17,8 @@ const apiService = { getVenueByID: async (id) => { const response = await axiosInstance.get(`/venues/details/${id}`) return response.data - } + }, + } export default apiService \ No newline at end of file From bb505112948fa5116a52520b67e1f9376c08fd35 Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Sun, 21 Jun 2026 10:20:55 +0530 Subject: [PATCH 28/95] order, payment --- BACKEND/app/api/booking_routes.py | 0 BACKEND/app/api/payment_routes.py | 27 ++++++++++ BACKEND/app/schema/payment.py | 4 ++ BACKEND/app/schema/venue.py | 1 - BACKEND/app/services/booking_service.py | 36 ++++++++++++++ BACKEND/app/services/order_service.py | 66 +++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 1 deletion(-) create mode 100644 BACKEND/app/api/booking_routes.py diff --git a/BACKEND/app/api/booking_routes.py b/BACKEND/app/api/booking_routes.py new file mode 100644 index 000000000..e69de29bb diff --git a/BACKEND/app/api/payment_routes.py b/BACKEND/app/api/payment_routes.py index 4ea1fca3c..596e7882e 100644 --- a/BACKEND/app/api/payment_routes.py +++ b/BACKEND/app/api/payment_routes.py @@ -24,6 +24,16 @@ def create_order(payload: CreateOrderRequest): order = client.order.create(data=order_data) + order = add_order_details( + db, + user_id=payload.user_id, + venue_id=payload.venue_id, + razorpay_order_id=order["id"], + amount=order_data["amount"], + currency=order_data["currency"], + status="pending", + ) + return { "order_id": order["id"], "amount": order["amount"], @@ -44,6 +54,23 @@ def verify_payment(payload: VerifyPaymentRequest): "razorpay_signature": payload.razorpay_signature }) + update_order_payment_status( + db, + razorpay_order_id=payload.razorpay_order_id, + razorpay_payment_id=payload.razorpay_payment_id, + status="paid" + ) + + create_booking( + db, + user_id=payload.user_id, + venue_id=payload.venue_id, + order_id=payload.razorpay_order_id, + booking_date=datetime.now().date(), + booking_time=datetime.now().strftime("%H:%M:%S"), + status="confirmed" + ) + return { "success": True, "message": "Payment verified successfully" diff --git a/BACKEND/app/schema/payment.py b/BACKEND/app/schema/payment.py index 72624a5e1..0dde31190 100644 --- a/BACKEND/app/schema/payment.py +++ b/BACKEND/app/schema/payment.py @@ -1,10 +1,14 @@ from pydantic import BaseModel class CreateOrderRequest(BaseModel): + user_id: int + venue_id: int amount: int # amount in rupees class VerifyPaymentRequest(BaseModel): + user_id: int + venue_id: int razorpay_order_id: str razorpay_payment_id: str razorpay_signature: str \ No newline at end of file diff --git a/BACKEND/app/schema/venue.py b/BACKEND/app/schema/venue.py index b260c801f..43e19d691 100644 --- a/BACKEND/app/schema/venue.py +++ b/BACKEND/app/schema/venue.py @@ -7,7 +7,6 @@ class VenueDetailsCreate(BaseModel): venue_description: str = Field(..., min_length=1) location: str = Field(..., min_length=1) capacity: int = Field(..., gt=0) - venue_price: int = Field(..., ge=0) venue_availabilty: Literal["hourly", "daily"] class VenueAmenitiesCreate(BaseModel): diff --git a/BACKEND/app/services/booking_service.py b/BACKEND/app/services/booking_service.py index e69de29bb..9b53cdb8e 100644 --- a/BACKEND/app/services/booking_service.py +++ b/BACKEND/app/services/booking_service.py @@ -0,0 +1,36 @@ +from fastapi import HTTPException, status +from app.core.config import settings +from typing import List +from sqlalchemy.orm import Session +from app.model.bookings import Booking + +def create_booking( + db: Session, + user_id: int, + venue_id: int, + order_id: str, + booking_date: str, + booking_time: str, + status: str +): + try: + new_booking = Booking( + user_id=user_id, + venue_id=venue_id, + order_id=order_id, + booking_date=booking_date, + booking_time=booking_time, + status=status + ) + + db.add(new_booking) + db.commit() + db.refresh(new_booking) + + return new_booking + + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error creating booking: {e}" + ) \ No newline at end of file diff --git a/BACKEND/app/services/order_service.py b/BACKEND/app/services/order_service.py index e69de29bb..d05441b46 100644 --- a/BACKEND/app/services/order_service.py +++ b/BACKEND/app/services/order_service.py @@ -0,0 +1,66 @@ +from fastapi import HTTPException, status +from app.core.config import settings +from typing import List +from sqlalchemy.orm import Session +from app.model.orders import Order + +async def add_order_details( + db: Session, + user_id: int, + venue_id: int, + razorpay_order_id: str, + amount: int, + currency: str, + status: str +): + try: + new_order = Order( + user_id=user_id, + venue_id=venue_id, + razorpay_order_id=razorpay_order_id, + amount=amount, + currency=currency, + status=status + ) + + db.add(new_order) + db.commit() + db.refresh(new_order) + + return new_order + + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error adding order details: {e}" + ) + + +async def update_order_payment_status( + db: Session, + razorpay_order_id: str, + razorpay_payment_id: str, + status: str +): + try: + order = db.query(Order).filter(Order.razorpay_order_id == razorpay_order_id).first() + + if not order: + raise HTTPException( + status_code=status.HTTP_404_NOT_FOUND, + detail="Order not found" + ) + + order.razorpay_payment_id = razorpay_payment_id + order.status = status + + db.commit() + db.refresh(order) + + return order + + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error updating order payment status: {e}" + ) \ No newline at end of file From ef2339c4fdffa782cc32bb096fbee3c28132b02c Mon Sep 17 00:00:00 2001 From: malik-dinar Date: Wed, 24 Jun 2026 23:10:36 +0530 Subject: [PATCH 29/95] booking, order --- BACKEND/app/api/booking_routes.py | 55 +++++++++++++++++++++++++ BACKEND/app/api/payment_routes.py | 18 +++++++- BACKEND/app/api/user_routes.py | 24 ++++++++++- BACKEND/app/api/venue_routes.py | 4 -- BACKEND/app/model/orders.py | 4 ++ BACKEND/app/model/venue.py | 4 +- BACKEND/app/model/venue_availability.py | 2 +- BACKEND/app/schema/venue.py | 2 - BACKEND/app/services/booking_service.py | 28 +++++++++++++ BACKEND/app/services/user_service.py | 23 +++++++++++ BACKEND/app/services/venue_service.py | 14 ++----- 11 files changed, 155 insertions(+), 23 deletions(-) create mode 100644 BACKEND/app/services/user_service.py diff --git a/BACKEND/app/api/booking_routes.py b/BACKEND/app/api/booking_routes.py index e69de29bb..0c5976e04 100644 --- a/BACKEND/app/api/booking_routes.py +++ b/BACKEND/app/api/booking_routes.py @@ -0,0 +1,55 @@ +from fastapi import APIRouter, Depends +from app.core.dependencies import get_current_user + + +router = APIRouter( + prefix="/booking", + tags=["Booking"] +) + +@router.get("/my-bookings") +async def get_my_bookings( + current_user: dict = Depends(get_current_user), + page_no: int = Query(1, ge=1, description="Page number"), + limit: int = Query(20, ge=1, le=100, description="Records per page"), + db: Session = Depends(get_db) +): + """ + Get all bookings for the current user. + """ + try: + return get_booking( + db, + user_id=current_user["id"], + venue_id=None, + page_no=page_no, + limit=limit + ) + + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + + +@router.get("/bookings/{venue_id}") +async def get_venue_bookings( + current_user: dict = Depends(get_current_user), + venue_id: int = Path(..., description="Venue ID"), + page_no: int = Query(1, ge=1, description="Page number"), + limit: int = Query(20, ge=1, le=100, description="Records per page"), + db: Session = Depends(get_db) +): + """ + Get all bookings for the current user. + """ + try: + return get_booking( + db, + user_id=current_user["id"], + venue_id=venue_id, + page_no=page_no, + limit=limit + ) + + except Exception as e: + raise HTTPException(status_code=400, detail=str(e)) + diff --git a/BACKEND/app/api/payment_routes.py b/BACKEND/app/api/payment_routes.py index 596e7882e..4ea45eaf8 100644 --- a/BACKEND/app/api/payment_routes.py +++ b/BACKEND/app/api/payment_routes.py @@ -2,6 +2,20 @@ import razorpay from fastapi import APIRouter, HTTPException from app.core.config import settings +from app.db.session import get_db +from sqlalchemy.orm import Session +from app.schema.payment import ( + CreateOrderRequest, + VerifyPaymentRequest, + PaymentFailedRequest +) +from app.services.order_service import ( + add_order_details, + update_order_payment_status, +) +from app.services.booking_service import create_booking +from datetime import datetime + router = APIRouter(prefix="/payments", tags=["Payments"]) @@ -13,7 +27,7 @@ ) @router.post("/create-order") -def create_order(payload: CreateOrderRequest): +def create_order(payload: CreateOrderRequest, db: Session = Depends(get_db)): try: order_data = { "amount": payload.amount * 100, # ₹500 => 50000 paise @@ -46,7 +60,7 @@ def create_order(payload: CreateOrderRequest): @router.post("/verify-payment") -def verify_payment(payload: VerifyPaymentRequest): +def verify_payment(payload: VerifyPaymentRequest, db: Session = Depends(get_db)): try: client.utility.verify_payment_signature({ "razorpay_order_id": payload.razorpay_order_id, diff --git a/BACKEND/app/api/user_routes.py b/BACKEND/app/api/user_routes.py index 39978deb5..436adead8 100644 --- a/BACKEND/app/api/user_routes.py +++ b/BACKEND/app/api/user_routes.py @@ -1,5 +1,8 @@ from fastapi import APIRouter, Depends from app.core.dependencies import get_current_user +from sqlalchemy.orm import Session +from app.db.session import get_db +from app.services.user_service import get_users_list router = APIRouter( prefix="/user", @@ -13,4 +16,23 @@ def profile( return { "message": "Authorized", "user": current_user - } \ No newline at end of file + } + +@router.get("/users") +def get_users( + current_user=Depends(get_current_user), + db: Session = Depends(get_db), + page_no: int = 1, + limit: int = 20 +): + try: + return get_users_list( + db, + page_no, + limit + ) + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) \ No newline at end of file diff --git a/BACKEND/app/api/venue_routes.py b/BACKEND/app/api/venue_routes.py index 09067ac90..d6004e598 100644 --- a/BACKEND/app/api/venue_routes.py +++ b/BACKEND/app/api/venue_routes.py @@ -82,8 +82,6 @@ def upload_venue_details( venue_description=payload.venue_description, location=payload.location, capacity=payload.capacity, - venue_price=payload.venue_price, - venue_availabilty=payload.venue_availabilty, ) except Exception as e: raise HTTPException( @@ -176,8 +174,6 @@ def update_venue_basic_details( venue_description=payload.venue_description, location=payload.location, capacity=payload.capacity, - venue_price=payload.venue_price, - venue_availabilty=payload.venue_availabilty, ) except Exception as e: raise HTTPException( diff --git a/BACKEND/app/model/orders.py b/BACKEND/app/model/orders.py index ce46f4c7d..4326a0597 100644 --- a/BACKEND/app/model/orders.py +++ b/BACKEND/app/model/orders.py @@ -25,6 +25,10 @@ class Order(Base): ) # status can be "pending", "paid", "failed", "refunded" + refunded_amount = Column(Integer, nullable=False, default=0) # store paise, not rupees + refund_reason = Column(String(255), nullable=True) + refund_percentage = Column(Integer, nullable=True) # store percentage as an integer (e.g., 50 for 50%) + payment_time = Column(DateTime(timezone=True), nullable=True) created_at = Column(DateTime(timezone=True), server_default=func.now()) diff --git a/BACKEND/app/model/venue.py b/BACKEND/app/model/venue.py index 423af5652..10f6cb870 100644 --- a/BACKEND/app/model/venue.py +++ b/BACKEND/app/model/venue.py @@ -39,9 +39,9 @@ class Venue(Base): ) user = relationship("User", back_populates="venues") - venue_amenities = relationship("VenueAmenities", back_populates="venue", cascade="all, delete-orphan") + venue_amenities = relationship("VenueAmenities", back_populates="venue", uselist=False, cascade="all, delete-orphan") venue_images = relationship("VenueImages", back_populates="venue", cascade="all, delete-orphan") - venue_availability = relationship("VenueAvailability", back_populates="venue", cascade="all, delete-orphan") + venue_availability = relationship("VenueAvailability", back_populates="venue", uselist=False, cascade="all, delete-orphan") orders = relationship("Order", back_populates="venue", cascade="all, delete-orphan") bookings = relationship("Booking", back_populates="venue", cascade="all, delete-orphan") diff --git a/BACKEND/app/model/venue_availability.py b/BACKEND/app/model/venue_availability.py index ccb928329..cebffdae8 100644 --- a/BACKEND/app/model/venue_availability.py +++ b/BACKEND/app/model/venue_availability.py @@ -14,7 +14,7 @@ class VenueAvailability(Base): __tablename__ = "venue_availability" - id = Column(Integer, primary_key=True, index=True) + id = Column(Integer, primary_key=True, unique=True, index=True) venue_id = Column( Integer, ForeignKey("venues.id", ondelete="CASCADE"), diff --git a/BACKEND/app/schema/venue.py b/BACKEND/app/schema/venue.py index 43e19d691..ecb49472a 100644 --- a/BACKEND/app/schema/venue.py +++ b/BACKEND/app/schema/venue.py @@ -7,7 +7,6 @@ class VenueDetailsCreate(BaseModel): venue_description: str = Field(..., min_length=1) location: str = Field(..., min_length=1) capacity: int = Field(..., gt=0) - venue_availabilty: Literal["hourly", "daily"] class VenueAmenitiesCreate(BaseModel): wifi: bool = False @@ -32,7 +31,6 @@ class VenueDetailsUpdate(BaseModel): location: Optional[str] = None capacity: Optional[int] = None venue_price: Optional[int] = None - venue_availabilty: Optional[str] = None class VenueAmenitiesUpdate(BaseModel): wifi: bool = False diff --git a/BACKEND/app/services/booking_service.py b/BACKEND/app/services/booking_service.py index 9b53cdb8e..fb3488519 100644 --- a/BACKEND/app/services/booking_service.py +++ b/BACKEND/app/services/booking_service.py @@ -33,4 +33,32 @@ def create_booking( raise HTTPException( status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=f"Error creating booking: {e}" + ) + +def get_booking( + db: Session, + user_id: int = None, + venue_id: int = None, + page_no: int = 1, + limit: int = 20 +) -> List[Booking]: + try: + query = db.query(Booking) + + if user_id is not None: + query = query.filter(Booking.user_id == user_id) + + if venue_id is not None: + query = query.filter(Booking.venue_id == venue_id) + + # Apply pagination + query = query.offset((page_no - 1) * limit).limit(limit) + + bookings = query.all() + return bookings + + except Exception as e: + raise HTTPException( + status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, + detail=f"Error retrieving bookings: {e}" ) \ No newline at end of file diff --git a/BACKEND/app/services/user_service.py b/BACKEND/app/services/user_service.py new file mode 100644 index 000000000..faa716a3c --- /dev/null +++ b/BACKEND/app/services/user_service.py @@ -0,0 +1,23 @@ +from sqlalchemy.orm import Session + +from app.model.venue import Venue +from app.model.user import User +from app.model.venue_amenities import VenueAmenities +from app.model.venue_images import VenueImages +from app.model.venue_availability import VenueAvailability +from typing import List +from sqlalchemy import or_ + +def get_users_list( + db: Session, page_no: int = 1, limit: int = 20 +) -> List[User]: + try: + offset = (page_no - 1) * limit + users = db.query(User).offset(offset).limit(limit).all() + return users + except Exception as e: + raise HTTPException( + status_code=400, + detail=str(e) + ) + diff --git a/BACKEND/app/services/venue_service.py b/BACKEND/app/services/venue_service.py index 8ca0f3a1e..7383381ee 100644 --- a/BACKEND/app/services/venue_service.py +++ b/BACKEND/app/services/venue_service.py @@ -30,7 +30,7 @@ def get_venues( for venue in venues: first_image = venue.venue_images[0] if venue.venue_images else None - price = venue.venue_availability[0].venue_price if venue.venue_availability else None + price = venue.venue_availability.venue_price if venue.venue_availability else None result.append({ "id": venue.id, @@ -69,7 +69,7 @@ def get_venue_details_by_id( "message": "venues details is not present" } - venue_price = venue.venue_availability[0].venue_price if venue.venue_availability else None + venue_price = venue.venue_availability.venue_price if venue.venue_availability else None return { "id": venue.id, @@ -253,8 +253,6 @@ def edit_venue( venue_description: str = None, location: str = None, capacity: int = None, - venue_price: int = None, - venue_availabilty: str = None, ): venue = ( @@ -278,12 +276,6 @@ def edit_venue( if capacity is not None: venue.capacity = capacity - if venue_price is not None: - venue.venue_price = venue_price - - if venue_availabilty is not None: - venue.venue_availabilty = venue_availabilty - db.commit() db.refresh(venue) @@ -394,7 +386,7 @@ def search_venues( for venue in venues: first_image = venue.venue_images[0] if venue.venue_images else None - price = venue.venue_availability[0].venue_price if venue.venue_availability else None + price = venue.venue_availability.venue_price if venue.venue_availability else None result.append({ "id": venue.id, From 6746a4367f9b57aebc8c31da332a4df2ff97a60c Mon Sep 17 00:00:00 2001 From: nafanpe Date: Sat, 27 Jun 2026 11:44:28 +0530 Subject: [PATCH 30/95] latest udpates --- FRONTEND/index.html | 2 +- FRONTEND/src/App.jsx | 2 + FRONTEND/src/components/Filters.jsx | 24 +- FRONTEND/src/components/FormBooking.jsx | 8 +- FRONTEND/src/components/VenueGrid.jsx | 140 ++++--- FRONTEND/src/hooks/useAuthForm.jsx | 3 + FRONTEND/src/pages/ListNewVenues.jsx | 509 ++++++++++++++++++++++++ FRONTEND/src/pages/OwnerDashboard.jsx | 18 +- FRONTEND/src/pages/VenueListing.jsx | 40 +- FRONTEND/src/services/apiService.js | 25 +- 10 files changed, 677 insertions(+), 94 deletions(-) create mode 100644 FRONTEND/src/pages/ListNewVenues.jsx diff --git a/FRONTEND/index.html b/FRONTEND/index.html index 2c738df8c..1aeab26cb 100644 --- a/FRONTEND/index.html +++ b/FRONTEND/index.html @@ -4,7 +4,7 @@ - frontend + BookMyVenue
diff --git a/FRONTEND/src/App.jsx b/FRONTEND/src/App.jsx index 89e03f054..ba6f4b380 100644 --- a/FRONTEND/src/App.jsx +++ b/FRONTEND/src/App.jsx @@ -4,6 +4,7 @@ import LoginPage from "./pages/LoginPage" import SpaceListing from "./pages/VenueListing" import BookMyVenueLanding from "./pages/HostLandingPage" import OwnerDashboard from './pages/OwnerDashboard'; +import ListNewVenue from './pages/ListNewVenues'; function App() { @@ -17,6 +18,7 @@ function App() { } /> } /> } /> + } />
diff --git a/FRONTEND/src/components/Filters.jsx b/FRONTEND/src/components/Filters.jsx index ceaacb6e9..e077d8481 100644 --- a/FRONTEND/src/components/Filters.jsx +++ b/FRONTEND/src/components/Filters.jsx @@ -18,16 +18,16 @@ function Filters() { icon: , options: ["Downtown", "Suburbs", "City Center", "Beachfront"] }, - { - name: "Venue Type", - icon: , - options: ["Indoor", "Outdoor", "Rooftop", "Barn"] - }, - { - name: "Budget", - icon: , - options: ["$ (Under $500)", "$$ ($500 - $1k)", "$$$ ($1k - $5k)", "$$$$ ($5k+)"] - }, + // { + // name: "Venue Type", + // icon: , + // options: ["Indoor", "Outdoor", "Rooftop", "Barn"] + // }, + // { + // name: "Budget", + // icon: , + // options: ["$ (Under $500)", "$$ ($500 - $1k)", "$$$ ($1k - $5k)", "$$$$ ($5k+)"] + // }, { name: "Amenities", icon: , @@ -90,10 +90,10 @@ function Filters() {
))} - + */}
) } diff --git a/FRONTEND/src/components/FormBooking.jsx b/FRONTEND/src/components/FormBooking.jsx index eff37b0c6..da8d6b7f4 100644 --- a/FRONTEND/src/components/FormBooking.jsx +++ b/FRONTEND/src/components/FormBooking.jsx @@ -46,8 +46,8 @@ export default function FormBooking ({ const endTime = new Date() - SetTimeFromString(startTime, venue.availability[0].open_time) - SetTimeFromString(endTime, venue.availability[0].closing_time) + SetTimeFromString(startTime, venue.availability.open_time) + SetTimeFromString(endTime, venue.availability.closing_time) while(startTime < endTime){ const startStr = startTime.toLocaleTimeString('en-US', {hour: 'numeric', minute: '2-digit', hour12: true}) @@ -64,8 +64,8 @@ export default function FormBooking ({ } const availTime = GenerateHourlyTimeIntervals(); - const isHourly = venue.availability.some(avail => avail.booking_types == "hourly") - const isDaily = venue.availability.some(avail => avail.booking_types == "daily") + const isHourly = venue.availability.booking_types == "hourly" + const isDaily = venue.availability.booking_types == "daily" return ( <> diff --git a/FRONTEND/src/components/VenueGrid.jsx b/FRONTEND/src/components/VenueGrid.jsx index df9be0516..f6eed9ff5 100644 --- a/FRONTEND/src/components/VenueGrid.jsx +++ b/FRONTEND/src/components/VenueGrid.jsx @@ -1,20 +1,25 @@ import { MapPin, SpinnerOne } from "@mynaui/icons-react"; -import { VENUE_DATA } from "../data/VenueCardData"; -import { useVenueGrid } from "../hooks/useVenueGrid" +import { useVenueGrid } from "../hooks/useVenueGrid"; import { useNavigate } from 'react-router-dom'; function GridHeader({ venueCount }) { return ( -
-
-

- Venues for you -

-
- {venueCount} venues found -
+
+
+
+

+ Venues for you +

+

+ Discover the perfect space for your next event +

+
+ +
+ {venueCount} venues available +
-
+
); } @@ -23,79 +28,100 @@ function VenueCard({ venue }) { const navigate = useNavigate(); const gridSpan = venue.isLarge ? "md:col-span-2 md:row-span-2" : "col-span-1"; - const titleSize = venue.isLarge ? "text-xl" : "text-sm"; - const detailSize = venue.isLarge ? "text-md" : "text-sm"; + const titleSize = venue.isLarge ? "text-2xl" : "text-lg"; + const detailSize = venue.isLarge ? "text-base" : "text-sm"; const priceSize = venue.isLarge ? "text-lg" : "text-sm"; - const imageSize = venue.isLarge ? "h-144" : "h-48"; + const imageSize = venue.isLarge ? "h-144" : "h-56"; // Slightly taller base height for better aspect ratio - // Variables to change in API INTERGRATION PHASE - const venue_name = venue.venue_name - const venue_image = venue.image - const venue_details = venue.venue_description - const venue_price = venue.venue_price + // Variables for API INTEGRATION PHASE + const venue_name = venue.venue_name; + const venue_image = venue.image; + const venue_details = venue.venue_description; + const venue_price = venue.venue_price; - return ( -
navigate(`/venues/${venue.id}`)} - className={`bg-[#f9f9f7] rounded-lg shadow-md overflow-hidden hover:shadow-lg transition-shadow duration-300 ease-in-out cursor-pointer ${gridSpan}`} + return ( +
navigate(`/venues/${venue.id}`)} + className={`group flex flex-col bg-white rounded-2xl border border-gray-100 shadow-sm hover:shadow-xl transition-shadow duration-300 ease-in-out cursor-pointer overflow-hidden ${gridSpan}`} > - {venue_name} + {/* Image Container */} +
+ {venue_name} + {/* Subtle dark overlay on hover instead of scaling */} +
+
-
-

- {venue_name} -

+ {/* Content Container */} +
+
+

+ {venue_name} +

+ + {/* Price Badge */} +
+ {venue_price} +
+
-
- -

- {venue_details} + {/* Location/Details anchored to bottom */} +

+ +

+ {venue_details}

- -

- {venue_price} -

-
+
); } - export default function VenueGrid() { - const {venues, isLoading, error} = useVenueGrid(); - const Data = venues // 'venues' for fetched api data [API INTEGRATION PHASE] + const { venues, isLoading, error } = useVenueGrid(); + const Data = Array.isArray(venues) ? venues : []; - if(error){ + if (error) { return ( -
-
-

Oops! Something went wrong.

-

{error.message}

+
+
+

Oops! Something went wrong.

+

{error.message}

- ) + ); } - if(isLoading){ + if (isLoading) { return (
- -

Loading your spaces...

+ +

Loading your spaces...

+
+ ); + } + + if (Data.length === 0) { + return ( +
+ +
+ +

No venues available right now

+

Check back later or try adjusting your search filters.

+
- ) + ); } return ( -
+
-
+
{Data.map((venue) => ( ))} diff --git a/FRONTEND/src/hooks/useAuthForm.jsx b/FRONTEND/src/hooks/useAuthForm.jsx index 3a6e1bdfa..0af854d16 100644 --- a/FRONTEND/src/hooks/useAuthForm.jsx +++ b/FRONTEND/src/hooks/useAuthForm.jsx @@ -73,6 +73,9 @@ export const useAuthForm = () => { if(data.access_token){ Cookies.set("authToken", data.access_token, {expires: 30, secure: true, sameSite: 'Lax'}) Cookies.set('userRole', data.user.role, {expires: 30, secure: true, sameSite: 'Lax'}) + Cookies.set('userId', data.user.id, {expires: 30, secure: true, sameSite: 'Lax'}) + Cookies.set('userEmail', data.user.email, {expires: 30, secure: true, sameSite: 'Lax'}) + Cookies.set('userName', data.user.name, {expires: 30, secure: true, sameSite: 'Lax'}) } setSuccessMessage(isLoginView ? 'Logged in successfully!' : 'Account created successfully!') diff --git a/FRONTEND/src/pages/ListNewVenues.jsx b/FRONTEND/src/pages/ListNewVenues.jsx new file mode 100644 index 000000000..691e2830a --- /dev/null +++ b/FRONTEND/src/pages/ListNewVenues.jsx @@ -0,0 +1,509 @@ +import React, { useRef, useState } from 'react'; +import { Link, useNavigate } from 'react-router-dom'; +import { + X, + Check, + Users, + ArrowLeft, + ArrowRight, + MapPin, + Building, + Wifi, + Coffee, + Truck, + SunSnow, + Monitor, + Wheelchair, + Upload, // Added for the upload zone + Image as ImageIcon, // Added for image placeholders + Trash // Added for delete button +} from '@mynaui/icons-react'; +import apiService from '../services/apiService'; +import Cookies from 'js-cookie'; + +export default function ListNewVenue() { + + const navigate = useNavigate() + + const [currentStep, setCurrentStep] = useState(1); + + const steps = ['Basic Info', 'Amenities', 'Photos', 'Availability']; + + const [venueId, setVenueId] = useState(null); + + const nextStep = () => setCurrentStep(prev => Math.min(prev + 1, 4)); + const prevStep = () => setCurrentStep(prev => Math.max(prev - 1, 1)); + + const InputImagesRef = useRef(null); + const [imageselected, setImagesSelected] = useState([]); + + const currentUserId = Cookies.get('userId'); + + const [basicFormData, setBasicFormData] = useState({ + user_id: currentUserId ? parseInt(currentUserId) : 1, + venue_name: "", + venue_description: "", + location: "", + capacity: 0, + }) + + const [amenitiesFormData, setAmenitiesFormData] = useState({ + wifi: false, + kitchen: false, + parking: false, + ac: false, + wheel_chair: false, + av_equipements: false + }) + + const [availabilityFormData, setAvailabilityFormData] = useState({ + booking_types: "hourly", + open_time: "", + closing_time: "", + minimum_hours: 0, + gap_between_bookings: 0, + venue_price: 0 + }) + + // for step 3 + const handleInputClick = () => { + InputImagesRef.current.click() + } + + // for step 3 + const handleFileSelect = (e) => { + const files = Array.from(e.target.files) + + const newImages = files.map(file => ({ + file: file, + preview: URL.createObjectURL(file) + })) + + setImagesSelected( prev => [...prev, ...newImages] ); + } + + // for step 3 + const removeImage = (indexToRemove) => { + setImagesSelected(prev => + prev.filter((_, index) => index !== indexToRemove) + ) + } + + // updates values for the form datas + const handleChange = (e) => { + const { name, value } = e.target; + + // Convert to number if it's a number field, otherwise keep as string + const numberFields = ['capacity', 'venue_price', 'minimum_hours', 'gap_between_bookings']; + const parsedValue = numberFields.includes(name) ? Number(value) : value; + + if (name in basicFormData) { + setBasicFormData(prev => ({ ...prev, [name]: parsedValue })); + } else if (name in availabilityFormData) { + setAvailabilityFormData(prev => ({ ...prev, [name]: parsedValue })); + } else { + console.warn(`Warning: Input name "${name}" was not found in either state object!`); + } + } + + // submitting the forms + const handleSubmit = async () => { + try { + if(currentStep === 1){ + const response = await apiService.postBasicVenueDetails(basicFormData) + setVenueId(response?.venue_id) + } else if (currentStep === 2){ + await apiService.postVenueAmenities(amenitiesFormData, venueId) + } else if (currentStep === 3){ + const formDataPayload = new FormData(); + imageselected.forEach((imageObj) => { + formDataPayload.append('images', imageObj.file); + }); + await apiService.postVenuePhotos(formDataPayload, venueId); + } else if (currentStep === 4){ + await apiService.postVenueAvailability(availabilityFormData, venueId) + } + + // API call was successful! + return true; + + } catch (error) { + console.error(error.response?.data || error) + alert("Something went wrong! Please check the console."); + + // API call failed! + return false; + } + } + + // for step 2 + const toggleAmenities = (amenity) => { + setAmenitiesFormData(prev => ({ + ...prev, + [amenity] : !prev[amenity] + })) + } + + return ( +
+ +
+ + + +

+ BookMyVenue +

+
+
+ +
+ +
+
+
+ +
+ + {steps.map((label, index) => { + const stepNumber = index + 1; + const isCompleted = stepNumber < currentStep; + const isActive = stepNumber === currentStep; + + return ( +
+
+ {isCompleted ? : stepNumber} +
+ + {label} + +
+ ); + })} +
+
+ +
+ + {/* STEP 1: BASIC INFO */} + {currentStep === 1 && ( +
+ {/* ... (Kept exactly as you had it) ... */} +

Let's start with the basics

+

What is the name and location of your property?

+ +
+
+ +
+ + + +
+
+ +
+ +
+ + +
+
+ +
+
+ +