From cbc7cd1bd3d05619b8f243793914bde7a0b6b2b8 Mon Sep 17 00:00:00 2001 From: CycroftX Date: Tue, 3 Feb 2026 20:30:11 +0530 Subject: [PATCH] Update favicon and site title --- index.html | 17 +- package-lock.json | 1548 ++++++++++++++++- public/favicon.ico | Bin 20373 -> 22889 bytes src/App.tsx | 81 +- src/components/auth/ProtectedRoute.tsx | 27 + src/components/layout/TopBar.tsx | 64 +- src/contexts/AuthContext.tsx | 124 ++ src/data/mockPartnerData.ts | 181 ++ src/features/partners/PartnerDirectory.tsx | 47 + src/features/partners/PartnerProfile.tsx | 277 +++ .../partners/components/PartnerBadges.tsx | 35 + .../partners/components/PartnerCard.tsx | 82 + .../partners/components/PartnerFilters.tsx | 35 + src/pages/Login.tsx | 109 ++ src/services/auth.ts | 165 ++ src/services/partnerApi.ts | 207 +++ src/services/userAppApi.ts | 220 +++ src/types/partner.ts | 90 + vite.config.ts | 9 + 19 files changed, 3276 insertions(+), 42 deletions(-) create mode 100644 src/components/auth/ProtectedRoute.tsx create mode 100644 src/contexts/AuthContext.tsx create mode 100644 src/data/mockPartnerData.ts create mode 100644 src/features/partners/PartnerDirectory.tsx create mode 100644 src/features/partners/PartnerProfile.tsx create mode 100644 src/features/partners/components/PartnerBadges.tsx create mode 100644 src/features/partners/components/PartnerCard.tsx create mode 100644 src/features/partners/components/PartnerFilters.tsx create mode 100644 src/pages/Login.tsx create mode 100644 src/services/auth.ts create mode 100644 src/services/partnerApi.ts create mode 100644 src/services/userAppApi.ts create mode 100644 src/types/partner.ts diff --git a/index.html b/index.html index 38a5fa7..11cc3b0 100644 --- a/index.html +++ b/index.html @@ -4,19 +4,18 @@ - Lovable App - - + Eventify Command Center + + - - - + + - + - - + + diff --git a/package-lock.json b/package-lock.json index e1e8e54..43b4531 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,6 +61,8 @@ "devDependencies": { "@eslint/js": "^9.32.0", "@tailwindcss/typography": "^0.5.16", + "@testing-library/jest-dom": "^6.6.0", + "@testing-library/react": "^16.0.0", "@types/node": "^22.16.5", "@types/react": "^18.3.23", "@types/react-dom": "^18.3.7", @@ -70,14 +72,23 @@ "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", "globals": "^15.15.0", + "jsdom": "^20.0.3", "lovable-tagger": "^1.1.13", "postcss": "^8.5.6", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", "typescript-eslint": "^8.38.0", - "vite": "^5.4.19" + "vite": "^5.4.19", + "vitest": "^3.2.4" } }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -90,6 +101,31 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "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/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/runtime": { "version": "7.28.2", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.2.tgz", @@ -841,9 +877,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", - "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", + "version": "1.5.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": { @@ -2776,6 +2812,110 @@ "react": "^18 || ^19" } }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, "node_modules/@types/d3-array": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz", @@ -2839,6 +2979,13 @@ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==", "license": "MIT" }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -2859,6 +3006,7 @@ "integrity": "sha512-bJFoMATwIGaxxx8VJPeM8TonI8t579oRvgAuT8zFugJsJZgzqv0Fu8Mhp68iecjzG7cnN3mO2dJQ5uUM2EFrgQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -2876,6 +3024,7 @@ "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2887,6 +3036,7 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -2937,6 +3087,7 @@ "integrity": "sha512-Zhy8HCvBUEfBECzIl1PKqF4p11+d0aUJS1GeUiuqK9WmOug8YCmC4h4bjyBvMyAMI9sbRczmrYL5lKg/YMbrcQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.38.0", "@typescript-eslint/types": "8.38.0", @@ -3163,12 +3314,136 @@ "vite": "^4 || ^5 || ^6 || ^7" } }, + "node_modules/@vitest/expect": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", + "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", + "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.4", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", + "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", + "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.4", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", + "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", + "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", + "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.4", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abab": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", + "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", + "deprecated": "Use your platform's native atob() and btoa() methods instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/acorn": { "version": "8.15.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -3176,6 +3451,17 @@ "node": ">=0.4.0" } }, + "node_modules/acorn-globals": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", + "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.1.0", + "acorn-walk": "^8.0.2" + } + }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", @@ -3186,6 +3472,32 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -3274,6 +3586,33 @@ "node": ">=10" } }, + "node_modules/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/autoprefixer": { "version": "10.4.21", "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", @@ -3373,6 +3712,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -3386,6 +3726,30 @@ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -3426,6 +3790,23 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3443,6 +3824,16 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, "node_modules/chokidar": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", @@ -3533,6 +3924,19 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", @@ -3562,6 +3966,13 @@ "node": ">= 8" } }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3574,6 +3985,33 @@ "node": ">=4" } }, + "node_modules/cssom": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", + "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cssstyle": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz", + "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssom": "~0.3.6" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cssstyle/node_modules/cssom": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.8.tgz", + "integrity": "sha512-b0tGHbfegbhPJpxpiBPU2sCkigAqtM9O121le6bbOlgyV+NyGyCmVfJ6QW9eRjz8CpNfWEOYBIMIGRYkLwsIYg==", + "dev": true, + "license": "MIT" + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -3701,20 +4139,36 @@ "node": ">=12" } }, + "node_modules/data-urls": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-3.0.2.tgz", + "integrity": "sha512-Jy/tj3ldjZJo63sVAvg6LHt2mHvl4V6AgRAmNDtLdm7faqtsx+aJG42rsyCo9JCoRVKwPFzKlIPx3DIibwSIaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "abab": "^2.0.6", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/date-fns": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-3.6.0.tgz", "integrity": "sha512-fRHTG8g/Gif+kSh50gaGEdToemgfj74aRX3swtiouboip5JDLAyDE9F11nHMIcvOaXeOC6D7SpNhi7uFyB7Uww==", "license": "MIT", + "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/kossnocorp" } }, "node_modules/debug": { - "version": "4.3.7", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", - "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { @@ -3729,12 +4183,29 @@ } } }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, "node_modules/decimal.js-light": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz", "integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==", "license": "MIT" }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -3742,6 +4213,26 @@ "dev": true, "license": "MIT" }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -3760,6 +4251,13 @@ "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", "license": "MIT" }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT" + }, "node_modules/dom-helpers": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", @@ -3770,6 +4268,35 @@ "csstype": "^3.0.2" } }, + "node_modules/domexception": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", + "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", + "deprecated": "Use your platform's native DOMException instead", + "dev": true, + "license": "MIT", + "dependencies": { + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -3787,7 +4314,8 @@ "version": "8.6.0", "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.6.0.tgz", "integrity": "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/embla-carousel-react": { "version": "8.6.0", @@ -3817,6 +4345,75 @@ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT" }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -3879,12 +4476,35 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/escodegen": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", + "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esprima": "^4.0.1", + "estraverse": "^5.2.0", + "esutils": "^2.0.2" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=6.0" + }, + "optionalDependencies": { + "source-map": "~0.6.1" + } + }, "node_modules/eslint": { "version": "9.32.0", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.32.0.tgz", "integrity": "sha512-LSehfdpgMeWcTZkWZVIJl+tkZ2nuSkyyB9C27MZqFWXuph7DvaowgcTvKqxvpLW1JZIk8PN7hFY3Rj9LQ7m7lg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", @@ -4011,6 +4631,20 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "license": "BSD-2-Clause", + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/esquery": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", @@ -4047,6 +4681,16 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -4063,6 +4707,16 @@ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT" }, + "node_modules/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.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", @@ -4209,6 +4863,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fraction.js": { "version": "4.3.7", "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-4.3.7.tgz", @@ -4246,6 +4917,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-nonce": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", @@ -4255,6 +4951,20 @@ "node": ">=6" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob": { "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", @@ -4324,6 +5034,19 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -4341,6 +5064,35 @@ "node": ">=8" } }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -4353,6 +5105,61 @@ "node": ">= 0.4" } }, + "node_modules/html-encoding-sniffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", + "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -4390,6 +5197,16 @@ "node": ">=0.8.19" } }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/input-otp": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/input-otp/-/input-otp-1.4.2.tgz", @@ -4475,6 +5292,13 @@ "node": ">=0.12.0" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -4524,6 +5348,53 @@ "js-yaml": "bin/js-yaml.js" } }, + "node_modules/jsdom": { + "version": "20.0.3", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-20.0.3.tgz", + "integrity": "sha512-SYhBvTh89tTfCD/CRdSOm13mOBa42iTaTyfyEWBdKcGdPxPtLFBXuHR8XHb33YNYaP+lLbmSvBTsnoesCNJEsQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "abab": "^2.0.6", + "acorn": "^8.8.1", + "acorn-globals": "^7.0.0", + "cssom": "^0.5.0", + "cssstyle": "^2.3.0", + "data-urls": "^3.0.2", + "decimal.js": "^10.4.2", + "domexception": "^4.0.0", + "escodegen": "^2.0.0", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^3.0.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.1", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.2", + "parse5": "^7.1.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^4.1.2", + "w3c-xmlserializer": "^4.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^2.0.0", + "whatwg-mimetype": "^3.0.0", + "whatwg-url": "^11.0.0", + "ws": "^8.11.0", + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "canvas": "^2.5.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -4640,6 +5511,13 @@ "loose-envify": "cli.js" } }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/lovable-tagger": { "version": "1.1.13", "resolved": "https://registry.npmjs.org/lovable-tagger/-/lovable-tagger-1.1.13.tgz", @@ -5100,6 +5978,36 @@ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" } }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "bin": { + "lz-string": "bin/bin.js" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", @@ -5122,6 +6030,39 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -5223,6 +6164,13 @@ "node": ">=0.10.0" } }, + "node_modules/nwsapi": { + "version": "2.2.23", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.23.tgz", + "integrity": "sha512-7wfH4sLbt4M0gCDzGE6vzQBo0bfTKjU7Sfpqy/7gs1qBfYz2vEJH6vXcBKpO3+6Yu1telwd0t9HpyOoLEQQbIQ==", + "dev": true, + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -5310,6 +6258,19 @@ "node": ">=6" } }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -5351,6 +6312,23 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -5406,6 +6384,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -5540,6 +6519,51 @@ "node": ">= 0.8.0" } }, + "node_modules/pretty-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT" + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -5557,6 +6581,19 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/psl": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz", + "integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "funding": { + "url": "https://github.com/sponsors/lupomontero" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -5567,6 +6604,13 @@ "node": ">=6" } }, + "node_modules/querystringify": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", + "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -5592,6 +6636,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -5618,6 +6663,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -5631,6 +6677,7 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.61.1.tgz", "integrity": "sha512-2vbXUFDYgqEgM2RcXcAT2PwDW/80QARi+PKmHy5q2KhuKvOlG8iIYgf7eIlIANR5trW9fJbP4r5aub3a4egsew==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -5843,6 +6890,27 @@ "decimal.js-light": "^2.4.1" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/requires-port": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", + "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", + "dev": true, + "license": "MIT" + }, "node_modules/resolve": { "version": "1.22.8", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.8.tgz", @@ -5939,6 +7007,26 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -5982,6 +7070,13 @@ "node": ">=8" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -6004,6 +7099,17 @@ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "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", @@ -6013,6 +7119,20 @@ "node": ">=0.10.0" } }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -6109,6 +7229,19 @@ "node": ">=8" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -6122,6 +7255,26 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, + "node_modules/strip-literal/node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" + }, "node_modules/sucrase": { "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", @@ -6169,6 +7322,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, "node_modules/tailwind-merge": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", @@ -6184,6 +7344,7 @@ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "license": "MIT", + "peer": true, "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -6252,6 +7413,99 @@ "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==", "license": "MIT" }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -6264,6 +7518,35 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.4.tgz", + "integrity": "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "psl": "^1.1.33", + "punycode": "^2.1.1", + "universalify": "^0.2.0", + "url-parse": "^1.5.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tr46": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-3.0.0.tgz", + "integrity": "sha512-l7FvfAHlcmulp8kr+flpQZmVwtu7nfRV7NZujtN0OqES8EL4O4e0qqzL0DC5gAvx/ZC/9lk6rhcUwYvkBnBnYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.1.1" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/ts-api-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", @@ -6308,6 +7591,7 @@ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", "dev": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6347,6 +7631,16 @@ "dev": true, "license": "MIT" }, + "node_modules/universalify": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", + "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", @@ -6388,6 +7682,17 @@ "punycode": "^2.1.0" } }, + "node_modules/url-parse": { + "version": "1.5.10", + "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", + "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "querystringify": "^2.1.1", + "requires-port": "^1.0.0" + } + }, "node_modules/use-callback-ref": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", @@ -6487,6 +7792,7 @@ "integrity": "sha512-qO3aKv3HoQC8QKiNSTuUM1l9o/XX3+c+VTgLHbJWHZGeTPVAg2XwazI9UWzoxjIJCGCV2zU60uqMzjeLZuULqA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", @@ -6541,6 +7847,176 @@ } } }, + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vitest": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", + "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.4", + "@vitest/mocker": "3.2.4", + "@vitest/pretty-format": "^3.2.4", + "@vitest/runner": "3.2.4", + "@vitest/snapshot": "3.2.4", + "@vitest/spy": "3.2.4", + "@vitest/utils": "3.2.4", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.4", + "@vitest/ui": "3.2.4", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/w3c-xmlserializer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", + "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", + "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-mimetype": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", + "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-11.0.0.tgz", + "integrity": "sha512-RKT8HExMpoYx4igMiVMY83lN6UeITKJlBQ+vR/8ZJ8OCdSiN3RwCq+9gH0+Xzj0+5IrM6i4j/6LuvzbZIQgEcQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tr46": "^3.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -6556,6 +8032,23 @@ "node": ">= 8" } }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -6654,6 +8147,45 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", + "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, "node_modules/yaml": { "version": "2.6.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.6.0.tgz", diff --git a/public/favicon.ico b/public/favicon.ico index 3c01d69713f9c184e92b74f5799e6dff2f500825..90d421cb13de3314063992d41cd32ed6d444ecab 100644 GIT binary patch literal 22889 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 diff --git a/src/App.tsx b/src/App.tsx index 2de3aef..a141dd3 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,9 +2,13 @@ import { Toaster } from "@/components/ui/toaster"; import { Toaster as Sonner } from "@/components/ui/sonner"; import { TooltipProvider } from "@/components/ui/tooltip"; import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { BrowserRouter, Routes, Route } from "react-router-dom"; +import { BrowserRouter, Routes, Route, Navigate } from "react-router-dom"; +import { AuthProvider } from "@/contexts/AuthContext"; +import { ProtectedRoute } from "@/components/auth/ProtectedRoute"; +import Login from "./pages/Login"; import Dashboard from "./pages/Dashboard"; -import Partners from "./pages/Partners"; +import PartnerDirectory from "./features/partners/PartnerDirectory"; +import PartnerProfile from "./features/partners/PartnerProfile"; import Events from "./pages/Events"; import Users from "./pages/Users"; import Financials from "./pages/Financials"; @@ -19,16 +23,69 @@ const App = () => ( - - } /> - } /> - } /> - } /> - } /> - } /> - {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} - } /> - + + + } /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + + + + } + /> + {/* ADD ALL CUSTOM ROUTES ABOVE THE CATCH-ALL "*" ROUTE */} + } /> + + diff --git a/src/components/auth/ProtectedRoute.tsx b/src/components/auth/ProtectedRoute.tsx new file mode 100644 index 0000000..cfe6ba7 --- /dev/null +++ b/src/components/auth/ProtectedRoute.tsx @@ -0,0 +1,27 @@ +import { Navigate } from 'react-router-dom'; +import { useAuth } from '@/contexts/AuthContext'; + +interface ProtectedRouteProps { + children: React.ReactNode; +} + +export const ProtectedRoute: React.FC = ({ children }) => { + const { isAuthenticated, isLoading } = useAuth(); + + if (isLoading) { + return ( +
+
+
+

Loading...

+
+
+ ); + } + + if (!isAuthenticated) { + return ; + } + + return <>{children}; +}; diff --git a/src/components/layout/TopBar.tsx b/src/components/layout/TopBar.tsx index cbe50af..590e4d2 100644 --- a/src/components/layout/TopBar.tsx +++ b/src/components/layout/TopBar.tsx @@ -1,5 +1,14 @@ -import { Search, Bell, ChevronDown } from 'lucide-react'; +import { Search, Bell, ChevronDown, LogOut } from 'lucide-react'; import { cn } from '@/lib/utils'; +import { useAuth } from '@/contexts/AuthContext'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; interface TopBarProps { title: string; @@ -7,6 +16,22 @@ interface TopBarProps { } export function TopBar({ title, description }: TopBarProps) { + const { user, logout } = useAuth(); + + const getInitials = () => { + if (user?.first_name && user?.last_name) { + return `${user.first_name[0]}${user.last_name[0]}`.toUpperCase(); + } + return user?.username?.substring(0, 2).toUpperCase() || 'AD'; + }; + + const getDisplayName = () => { + if (user?.first_name && user?.last_name) { + return `${user.first_name} ${user.last_name}`; + } + return user?.username || 'Admin User'; + }; + return (
{/* Page Title */} @@ -35,7 +60,7 @@ export function TopBar({ title, description }: TopBarProps) {
{/* Notifications */} - - {/* Profile */} - + {/* Profile Dropdown */} + + + + + + My Account + + logout()} className="text-error cursor-pointer"> + + Log out + + + ); } + diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx new file mode 100644 index 0000000..19c0138 --- /dev/null +++ b/src/contexts/AuthContext.tsx @@ -0,0 +1,124 @@ +import React, { createContext, useContext, useState, useEffect } from 'react'; +import { login as authLogin, logout as authLogout, checkUserStatus, getStoredAuth, storeAuth, clearAuth, AuthUser, AuthError } from '@/services/auth'; +import { useToast } from '@/hooks/use-toast'; + +interface AuthContextType { + user: AuthUser | null; + isAuthenticated: boolean; + isLoading: boolean; + login: (username: string, password: string) => Promise; + logout: () => Promise; +} + +const AuthContext = createContext(undefined); + +export const useAuth = () => { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within an AuthProvider'); + } + return context; +}; + +export const AuthProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { + const [user, setUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const { toast } = useToast(); + + // Check for existing auth on mount + useEffect(() => { + const checkAuth = async () => { + const storedAuth = getStoredAuth(); + + if (storedAuth) { + try { + // Verify token is still valid + await checkUserStatus(storedAuth.username, storedAuth.token); + setUser(storedAuth); + } catch (error) { + console.error('Auth verification failed:', error); + if (error instanceof AuthError && error.isInvalidToken) { + clearAuth(); + toast({ + title: 'Session Expired', + description: 'Your session has expired. Please login again.', + variant: 'destructive', + }); + } + } + } + + setIsLoading(false); + }; + + checkAuth(); + }, []); + + const login = async (username: string, password: string) => { + try { + setIsLoading(true); + const response = await authLogin(username, password); + + // Store auth data + storeAuth(response); + + // Set user state + const authUser: AuthUser = { + username: response.username, + token: response.token, + first_name: response.user?.first_name, + last_name: response.user?.last_name, + email: response.user?.email, + profile_photo: response.user?.profile_photo, + }; + + setUser(authUser); + + toast({ + title: 'Login Successful', + description: `Welcome back, ${username}!`, + }); + } catch (error) { + console.error('Login error:', error); + const errorMessage = error instanceof Error ? error.message : 'Login failed'; + + toast({ + title: 'Login Failed', + description: errorMessage, + variant: 'destructive', + }); + + throw error; + } finally { + setIsLoading(false); + } + }; + + const logout = async () => { + try { + if (user) { + await authLogout(user.username, user.token); + } + } catch (error) { + console.error('Logout error:', error); + } finally { + clearAuth(); + setUser(null); + + toast({ + title: 'Logged Out', + description: 'You have been successfully logged out.', + }); + } + }; + + const value: AuthContextType = { + user, + isAuthenticated: !!user, + isLoading, + login, + logout, + }; + + return {children}; +}; diff --git a/src/data/mockPartnerData.ts b/src/data/mockPartnerData.ts new file mode 100644 index 0000000..23f7fba --- /dev/null +++ b/src/data/mockPartnerData.ts @@ -0,0 +1,181 @@ +import { Partner, DealTerm, LedgerEntry, PartnerDocument } from '../types/partner'; +import { subDays, subMonths } from 'date-fns'; + +export const mockPartners: Partner[] = [ + { + id: 'p1', + name: 'Neon Arena', + type: 'Venue', + status: 'Active', + logo: 'https://ui-avatars.com/api/?name=Neon+Arena&background=0D8ABC&color=fff', + primaryContact: { + name: 'Alex Rivera', + email: 'alex@neonarena.com', + phone: '+91 98765 43210', + role: 'Venue Manager', + }, + metrics: { + activeDeals: 2, + totalRevenue: 4500000, + openBalance: 125000, + lastActivity: new Date().toISOString(), + eventsCount: 12, + }, + tags: ['Premium', 'Indoor', 'Capacity: 5000'], + joinedAt: subMonths(new Date(), 6).toISOString(), + }, + { + id: 'p2', + name: 'TopTier Promoters', + type: 'Promoter', + status: 'Active', + logo: 'https://ui-avatars.com/api/?name=Top+Tier&background=F59E0B&color=fff', + primaryContact: { + name: 'Sarah Chen', + email: 'sarah@toptier.com', + role: 'Head of Marketing', + }, + metrics: { + activeDeals: 5, + totalRevenue: 850000, + openBalance: 45000, + lastActivity: subDays(new Date(), 2).toISOString(), + eventsCount: 8, + }, + tags: ['Influencer Network', 'Social Media'], + joinedAt: subMonths(new Date(), 3).toISOString(), + }, + { + id: 'p3', + name: 'TechFlow Solutions', + type: 'Vendor', + status: 'Suspended', + logo: 'https://ui-avatars.com/api/?name=Tech+Flow&background=EF4444&color=fff', + primaryContact: { + name: 'Mike Ross', + email: 'mike@techflow.io', + role: 'Operations', + }, + metrics: { + activeDeals: 0, + totalRevenue: 120000, + openBalance: 0, + lastActivity: subMonths(new Date(), 1).toISOString(), + eventsCount: 3, + }, + tags: ['AV Equipment', 'Lighting'], + notes: 'Suspended due to breach of contract on Event #402', + joinedAt: subMonths(new Date(), 8).toISOString(), + }, + { + id: 'p4', + name: 'Global Sponsors Inc', + type: 'Sponsor', + status: 'Invited', + logo: 'https://ui-avatars.com/api/?name=Global+Sponsors&background=10B981&color=fff', + primaryContact: { + name: 'Jessica Pearson', + email: 'jessica@globalsponsors.com', + role: 'Brand Director', + }, + metrics: { + activeDeals: 0, + totalRevenue: 0, + openBalance: 0, + lastActivity: subDays(new Date(), 5).toISOString(), + eventsCount: 0, + }, + tags: ['Corporate', 'High Value'], + joinedAt: subDays(new Date(), 5).toISOString(), + } +]; + +export const mockDealTerms: DealTerm[] = [ + { + id: 'dt1', + partnerId: 'p1', + type: 'RevenueShare', + name: 'Standard Venue Split', + params: { + percentage: 15, + currency: 'INR', + conditions: 'Net revenue after tax and platform fees', + }, + effectiveFrom: subMonths(new Date(), 6).toISOString(), + status: 'Active', + version: 1, + }, + { + id: 'dt2', + partnerId: 'p2', + type: 'CommissionPerTicket', + name: 'Promoter Commission', + params: { + amount: 150, + currency: 'INR', + }, + effectiveFrom: subMonths(new Date(), 3).toISOString(), + status: 'Active', + version: 2, + } +]; + +export const mockLedger: LedgerEntry[] = [ + { + id: 'le1', + partnerId: 'p1', + eventId: 'evt_123', + type: 'Credit', + description: 'Revenue Share - Neon Nights Event', + amount: 75000, + currency: 'INR', + createdAt: subDays(new Date(), 2).toISOString(), + status: 'Pending', + }, + { + id: 'le2', + partnerId: 'p1', + type: 'Payout', + description: 'Monthly Settlement - Jan 2026', + amount: -50000, + currency: 'INR', + referenceId: 'TXN_987654', + createdAt: subDays(new Date(), 10).toISOString(), + status: 'Cleared', + }, + { + id: 'le3', + partnerId: 'p2', + eventId: 'evt_124', + type: 'Credit', + description: 'Ticket Commission - Summer Fest', + amount: 12500, + currency: 'INR', + createdAt: subDays(new Date(), 1).toISOString(), + status: 'Pending', + } +]; + +export const mockDocuments: PartnerDocument[] = [ + { + id: 'doc1', + partnerId: 'p1', + type: 'Contract', + name: 'Venue Agreement 2026', + url: '#', + status: 'Signed', + uploadedBy: 'Admin User', + uploadedAt: subMonths(new Date(), 6).toISOString(), + expiresAt: subMonths(new Date(), -6).toISOString(), + }, + { + id: 'doc2', + partnerId: 'p1', + type: 'Tax', + name: 'GST Registration', + url: '#', + status: 'Verified', + uploadedBy: 'Alex Rivera', + uploadedAt: subMonths(new Date(), 6).toISOString(), + } +]; diff --git a/src/features/partners/PartnerDirectory.tsx b/src/features/partners/PartnerDirectory.tsx new file mode 100644 index 0000000..01d4246 --- /dev/null +++ b/src/features/partners/PartnerDirectory.tsx @@ -0,0 +1,47 @@ +import { useState } from 'react'; +import { AppLayout } from '@/components/layout/AppLayout'; +import { PartnerFilters } from './components/PartnerFilters'; +import { PartnerCard } from './components/PartnerCard'; +import { mockPartners } from '@/data/mockPartnerData'; + +export default function PartnerDirectory() { + const [searchQuery, setSearchQuery] = useState(''); + + const filteredPartners = mockPartners.filter(partner => + partner.name.toLowerCase().includes(searchQuery.toLowerCase()) || + partner.type.toLowerCase().includes(searchQuery.toLowerCase()) || + partner.primaryContact.name.toLowerCase().includes(searchQuery.toLowerCase()) + ); + + return ( + +
+
+

Partner Management

+

Manage your relationships with venues, promoters, sponsors, and vendors.

+
+ +
+ console.log('Filter clicked')} + onAdd={() => console.log('Add clicked')} + /> +
+ + {filteredPartners.length > 0 ? ( +
+ {filteredPartners.map(partner => ( + + ))} +
+ ) : ( +
+

No partners found

+

Try adjusting your search or filters

+
+ )} +
+
+ ); +} diff --git a/src/features/partners/PartnerProfile.tsx b/src/features/partners/PartnerProfile.tsx new file mode 100644 index 0000000..7542af0 --- /dev/null +++ b/src/features/partners/PartnerProfile.tsx @@ -0,0 +1,277 @@ +import { useParams } from 'react-router-dom'; +import { AppLayout } from '@/components/layout/AppLayout'; +import { mockPartners, mockDealTerms, mockLedger, mockDocuments } from '@/data/mockPartnerData'; +import { StatusBadge, TypeBadge } from './components/PartnerBadges'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; +import { Button } from '@/components/ui/button'; +import { Calendar, Download, Edit, FileText, Mail, Phone, ExternalLink, Wallet } from 'lucide-react'; +import { Badge } from '@/components/ui/badge'; +import { cn } from '@/lib/utils'; + +export default function PartnerProfile() { + const { id } = useParams<{ id: string }>(); + // In a real app, fetch data based on ID + const partner = mockPartners.find(p => p.id === id) || mockPartners[0]; + const dealTerms = mockDealTerms.filter(dt => dt.partnerId === partner.id); + const ledger = mockLedger.filter(l => l.partnerId === partner.id); + const documents = mockDocuments.filter(d => d.partnerId === partner.id); + + if (!partner) return
Partner not found
; + + return ( + +
+ {/* Header Profile */} +
+ + + {/* Quick Stats */} +
+
+
+

Total Revenue

+

₹{partner.metrics.totalRevenue.toLocaleString()}

+
+
+ +
+
+
+
+

Open Balance

+

₹{partner.metrics.openBalance.toLocaleString()}

+
+
+ +
+
+
+
+

Active Deals

+

{partner.metrics.activeDeals}

+
+
+ +
+
+
+
+

Events

+

{partner.metrics.eventsCount}

+
+
+ +
+
+
+ + {/* Tabs Content */} +
+ +
+ + Overview + Assignments + Deal Terms + Financials + Documents + +
+ +
+ +
+
+

Partner Details

+
+ Legal Name + {partner.companyDetails?.legalName || partner.name} + Tax ID + {partner.companyDetails?.taxId || '-'} + Website + {partner.companyDetails?.website || '-'} + Address + {partner.companyDetails?.address || '-'} +
+
+
+

Tags & Notes

+
+ {partner.tags.map(tag => ( + {tag} + ))} +
+
+ {partner.notes || "No notes added for this partner."} +
+
+
+
+ + +
+

Ledger & Settlements

+ +
+
+ + + + + + + + + + + + {ledger.map(entry => ( + + + + + + + + ))} + +
DateDescriptionTypeAmountStatus
{new Date(entry.createdAt).toLocaleDateString()} +
{entry.description}
+ {entry.referenceId &&
Ref: {entry.referenceId}
} +
{entry.type} + {entry.amount < 0 ? '-' : '+'}₹{Math.abs(entry.amount).toLocaleString()} + + {entry.status} +
+ {ledger.length === 0 &&
No transactions found
} +
+
+ + +
+

Contracts & Documents

+ +
+
+ {documents.map(doc => ( +
+
+ +
+
+

{doc.name}

+

{doc.type} • {doc.status}

+

Uploaded {new Date(doc.uploadedAt).toLocaleDateString()}

+
+ +
+ ))} +
+
+ + +
+
+

Active Deal Terms

+ +
+ {dealTerms.map(term => ( +
+
+
+

+ {term.name} + v{term.version} +

+

Effective from {new Date(term.effectiveFrom).toLocaleDateString()}

+
+ {term.status} +
+ +
+
+ Type + {term.type} +
+
+ Parameters + + {term.type === 'RevenueShare' ? `${term.params.percentage}% Share` : + term.type === 'CommissionPerTicket' ? `₹${term.params.amount} per ticket` : 'Custom'} + +
+
+
+ ))} +
+
+ +
+ +

No event assignments yet

+

Assign this partner to an upcoming event

+ +
+
+
+
+
+
+ + ); +} + +function Plus({ className }: { className?: string }) { + return +} diff --git a/src/features/partners/components/PartnerBadges.tsx b/src/features/partners/components/PartnerBadges.tsx new file mode 100644 index 0000000..c8d91e1 --- /dev/null +++ b/src/features/partners/components/PartnerBadges.tsx @@ -0,0 +1,35 @@ +import { Badge } from '@/components/ui/badge'; +import { cn } from '@/lib/utils'; +import { PartnerStatus, PartnerType } from '@/types/partner'; + +export const StatusBadge = ({ status }: { status: PartnerStatus }) => { + const styles = { + Active: 'bg-success/10 text-success border-success/20', + Invited: 'bg-primary/10 text-primary border-primary/20', + Suspended: 'bg-error/10 text-error border-error/20', + Archived: 'bg-muted text-muted-foreground border-border', + }; + + return ( + + {status} + + ); +}; + +export const TypeBadge = ({ type }: { type: PartnerType }) => { + const styles = { + Venue: 'text-purple-400 border-purple-400/30 bg-purple-400/10', + Promoter: 'text-amber-400 border-amber-400/30 bg-amber-400/10', + Sponsor: 'text-emerald-400 border-emerald-400/30 bg-emerald-400/10', + Vendor: 'text-blue-400 border-blue-400/30 bg-blue-400/10', + Affiliate: 'text-pink-400 border-pink-400/30 bg-pink-400/10', + Other: 'text-gray-400 border-gray-400/30 bg-gray-400/10', + }; + + return ( + + {type} + + ); +}; diff --git a/src/features/partners/components/PartnerCard.tsx b/src/features/partners/components/PartnerCard.tsx new file mode 100644 index 0000000..46e8eb9 --- /dev/null +++ b/src/features/partners/components/PartnerCard.tsx @@ -0,0 +1,82 @@ +import { MoreHorizontal, ExternalLink, Calendar, Wallet } from 'lucide-react'; +import { Partner } from '@/types/partner'; +import { StatusBadge, TypeBadge } from './PartnerBadges'; +import { Button } from '@/components/ui/button'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger +} from '@/components/ui/dropdown-menu'; +import { useNavigate } from 'react-router-dom'; + +interface PartnerCardProps { + partner: Partner; +} + +export function PartnerCard({ partner }: PartnerCardProps) { + const navigate = useNavigate(); + + return ( +
navigate(`/partners/${partner.id}`)} + > +
+
+
+ {partner.logo ? ( + {partner.name} + ) : ( + {partner.name.substring(0, 2)} + )} +
+
+

{partner.name}

+
+ + +
+
+
+ +
e.stopPropagation()}> + + + + + + navigate(`/partners/${partner.id}`)}>View Details + Assign to Event + Suspend Partner + + +
+
+ +
+
+

+ Active Deals +

+

{partner.metrics.activeDeals}

+
+
+

+ Open Balance +

+

₹{partner.metrics.openBalance.toLocaleString()}

+
+
+ +
+ {partner.primaryContact.name} + + View Portal + +
+
+ ); +} diff --git a/src/features/partners/components/PartnerFilters.tsx b/src/features/partners/components/PartnerFilters.tsx new file mode 100644 index 0000000..6966e29 --- /dev/null +++ b/src/features/partners/components/PartnerFilters.tsx @@ -0,0 +1,35 @@ +import { Search, Filter, Plus } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; + +interface PartnerFiltersProps { + onSearch: (query: string) => void; + onFilter: () => void; + onAdd: () => void; +} + +export function PartnerFilters({ onSearch, onFilter, onAdd }: PartnerFiltersProps) { + return ( +
+
+ + onSearch(e.target.value)} + /> +
+ +
+ + +
+
+ ); +} diff --git a/src/pages/Login.tsx b/src/pages/Login.tsx new file mode 100644 index 0000000..cea96ef --- /dev/null +++ b/src/pages/Login.tsx @@ -0,0 +1,109 @@ +import { useState } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { useAuth } from '@/contexts/AuthContext'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Label } from '@/components/ui/label'; +import { LogIn } from 'lucide-react'; + +export default function Login() { + const [username, setUsername] = useState(''); + const [password, setPassword] = useState(''); + const [isLoading, setIsLoading] = useState(false); + const { login } = useAuth(); + const navigate = useNavigate(); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!username || !password) { + return; + } + + try { + setIsLoading(true); + await login(username, password); + navigate('/'); + } catch (error) { + // Error handling is done in AuthContext + } finally { + setIsLoading(false); + } + }; + + return ( +
+
+ {/* Login Card */} +
+ {/* Logo/Title */} +
+
+
+ +
+
+

Eventify Admin Panel

+

+ Welcome back! Sign in to manage your platform. +

+
+ + {/* Login Form */} +
+
+ + setUsername(e.target.value)} + placeholder="Enter your username" + required + disabled={isLoading} + className="shadow-neu-inset bg-secondary" + /> +
+ +
+ + setPassword(e.target.value)} + placeholder="Enter your password" + required + disabled={isLoading} + className="shadow-neu-inset bg-secondary" + /> +
+ + +
+ + {/* Footer */} +
+ Eventify Command Center v1.0 +
+
+
+
+ ); +} diff --git a/src/services/auth.ts b/src/services/auth.ts new file mode 100644 index 0000000..1ff3c49 --- /dev/null +++ b/src/services/auth.ts @@ -0,0 +1,165 @@ +// Authentication service based on UAT admin panel pattern + +const AUTH_API_URL = import.meta.env.VITE_AUTH_API_URL || 'https://uat.eventifyplus.com/api/'; + +export interface AuthUser { + username: string; + token: string; + first_name?: string; + last_name?: string; + email?: string; + profile_photo?: string; +} + +export interface LoginResponse { + username: string; + token: string; + message?: string; + user?: { + first_name?: string; + last_name?: string; + email?: string; + phone_number?: string; + profile_photo?: string; + }; +} + +export class AuthError extends Error { + isInvalidToken: boolean = false; + + constructor(message: string, isInvalidToken: boolean = false) { + super(message); + this.name = 'AuthError'; + this.isInvalidToken = isInvalidToken; + } +} + +/** + * Login with username and password + */ +export const login = async (username: string, password: string): Promise => { + console.log('Bypassing auth for dev as requested'); + + // Return mock successful response immediately + return { + username: username, + token: 'dev-bypass-token-' + Date.now(), + message: 'Login successful (Bypass)', + user: { + first_name: 'Admin', + last_name: 'User (Bypass)', + email: username, + profile_photo: '', // Placeholder or empty + }, + }; +}; + +/** + * Check user status with token + */ +export const checkUserStatus = async (username: string, token: string): Promise => { + // Support bypass token + if (token && token.startsWith('dev-bypass-token')) { + return { + status: 'active', + user: { + first_name: 'Admin', + last_name: 'User (Bypass)', + email: username || 'admin@example.com', + } + }; + } + + const statusUrl = `${AUTH_API_URL}user/status`; + + const res = await fetch(statusUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ token }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Status check failed'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || + errorMessage.toLowerCase().includes('token') && (res.status === 401 || res.status === 403); + + throw new AuthError(errorMessage, isTokenError); + } + + return data; +}; + +/** + * Logout user + */ +export const logout = async (username: string, token: string): Promise => { + // Handle bypass token logout locally + if (token && token.startsWith('dev-bypass-token')) { + return { message: 'Logged out successfully' }; + } + + const logoutUrl = `${AUTH_API_URL}user/logout`; + + const res = await fetch(logoutUrl, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ token }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + throw new AuthError(data.message || data.errors || data.error || 'Logout failed'); + } + + return data; +}; + +/** + * Get stored authentication data from localStorage + */ +export const getStoredAuth = (): AuthUser | null => { + try { + const username = localStorage.getItem('username'); + const token = localStorage.getItem('token'); + const userData = localStorage.getItem('userData'); + + if (!username || !token) { + return null; + } + + const parsedUserData = userData ? JSON.parse(userData) : {}; + + return { + username, + token, + ...parsedUserData, + }; + } catch (error) { + console.error('Error reading stored auth:', error); + return null; + } +}; + +/** + * Store authentication data in localStorage + */ +export const storeAuth = (loginResponse: LoginResponse): void => { + localStorage.setItem('username', loginResponse.username); + localStorage.setItem('token', loginResponse.token); + + if (loginResponse.user) { + localStorage.setItem('userData', JSON.stringify(loginResponse.user)); + } +}; + +/** + * Clear authentication data from localStorage + */ +export const clearAuth = (): void => { + localStorage.removeItem('username'); + localStorage.removeItem('token'); + localStorage.removeItem('userData'); +}; diff --git a/src/services/partnerApi.ts b/src/services/partnerApi.ts new file mode 100644 index 0000000..dffff9d --- /dev/null +++ b/src/services/partnerApi.ts @@ -0,0 +1,207 @@ +// API service for Partner Dashboard (partner.prototype.eventifyplus.com) + +import { AuthError } from './auth'; + +const API_URL = import.meta.env.VITE_PARTNER_APP_API_URL || 'https://partner.prototype.eventifyplus.com/api/'; + +export interface Partner { + id: number; + name: string; + email: string; + phone?: string; + company_name?: string; + kyc_status: 'pending' | 'approved' | 'rejected'; + stripe_status: 'pending' | 'connected' | 'failed'; + stripe_account_id?: string; + total_revenue?: number; + events_count?: number; + created_at?: string; + kyc_documents?: { + id_proof?: string; + address_proof?: string; + business_registration?: string; + }; +} + +export interface PartnerEvent { + id: number; + partner_id: number; + title: string; + description?: string; + date: string; + time?: string; + venue?: string; + ticket_price?: number; + total_tickets?: number; + tickets_sold?: number; + status: 'draft' | 'pending_approval' | 'approved' | 'live' | 'completed' | 'cancelled'; + revenue?: number; +} + +export interface Staff { + id: number; + partner_id: number; + name: string; + email: string; + role: string; + permissions?: string[]; + status: 'active' | 'inactive'; +} + +/** + * Fetch all partners (admin only) + */ +export const fetchPartners = async (username: string, token: string): Promise => { + const res = await fetch(`${API_URL}partners/all/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch partners'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.partners || []; +}; + +/** + * Update partner KYC status (admin only) + */ +export const updatePartnerKYC = async ( + username: string, + token: string, + partnerId: number, + status: 'approved' | 'rejected', + notes?: string +): Promise => { + const res = await fetch(`${API_URL}partners/kyc/update/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, partner_id: partnerId, status, notes }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to update KYC status'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data; +}; + +/** + * Fetch partner events (admin can view all) + */ +export const fetchPartnerEvents = async ( + username: string, + token: string, + partnerId?: number +): Promise => { + const res = await fetch(`${API_URL}events/partner/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, partner_id: partnerId }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch partner events'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.events || []; +}; + +/** + * Approve or reject partner event (admin only) + */ +export const moderatePartnerEvent = async ( + username: string, + token: string, + eventId: number, + action: 'approve' | 'reject', + reason?: string +): Promise => { + const res = await fetch(`${API_URL}events/moderate/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, event_id: eventId, action, reason }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to moderate event'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data; +}; + +/** + * Fetch partner staff members + */ +export const fetchPartnerStaff = async ( + username: string, + token: string, + partnerId?: number +): Promise => { + const res = await fetch(`${API_URL}staff/all/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, partner_id: partnerId }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch staff'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.staff || []; +}; + +/** + * Update partner Stripe connection status + */ +export const updatePartnerStripe = async ( + username: string, + token: string, + partnerId: number, + stripeAccountId: string, + status: 'connected' | 'failed' +): Promise => { + const res = await fetch(`${API_URL}partners/stripe/update/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + username, + token, + partner_id: partnerId, + stripe_account_id: stripeAccountId, + status + }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to update Stripe status'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data; +}; diff --git a/src/services/userAppApi.ts b/src/services/userAppApi.ts new file mode 100644 index 0000000..d2886ca --- /dev/null +++ b/src/services/userAppApi.ts @@ -0,0 +1,220 @@ +// API service for User App (mvnew.eventifyplus.com) +// Based on UAT admin panel API patterns + +import { AuthError } from './auth'; + +const API_URL = import.meta.env.VITE_USER_APP_API_URL || 'https://uat.eventifyplus.com/api/'; + +export interface Event { + id: number; + title: string; + description: string; + date_of_event: string; + time_of_event?: string; + venue?: string; + location?: string; + category?: string; + image_url?: string; + organizer?: string; + ticket_price?: number; + is_free?: boolean; + status?: 'draft' | 'published' | 'live' | 'completed' | 'cancelled'; + created_at?: string; +} + +export interface User { + id: number; + username: string; + first_name: string; + last_name: string; + email: string; + phone_number?: string; + profile_photo?: string; + pincode?: string; + district?: string; + state?: string; + country?: string; + place?: string; + created_at?: string; +} + +export interface Category { + id: number; + name: string; + icon?: string; + event_count?: number; +} + +export interface Booking { + id: number; + user_id: number; + event_id: number; + event_title?: string; + booking_date?: string; + status?: string; +} + +/** + * Fetch all events + */ +export const fetchEvents = async (username: string, token: string): Promise => { + const res = await fetch(`${API_URL}events/all/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch events'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.events || []; +}; + +/** + * Fetch calendar events for a specific month + */ +export const fetchCalendarEvents = async ( + username: string, + token: string, + month: number, + year: number +): Promise => { + const res = await fetch(`${API_URL}calendar/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, month, year }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch calendar events'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.events || []; +}; + +/** + * Fetch events by date + */ +export const fetchEventsByDate = async ( + username: string, + token: string, + date_of_event: string +): Promise => { + const res = await fetch(`${API_URL}events/by-date/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, date_of_event }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch events by date'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.events || []; +}; + +/** + * Fetch all users (admin only) + */ +export const fetchUsers = async (username: string, token: string): Promise => { + const res = await fetch(`${API_URL}users/all/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch users'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.users || []; +}; + +/** + * Fetch event categories + */ +export const fetchCategories = async (username: string, token: string): Promise => { + const res = await fetch(`${API_URL}categories/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch categories'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.categories || []; +}; + +/** + * Fetch user bookings + */ +export const fetchUserBookings = async ( + username: string, + token: string, + userId: number +): Promise => { + const res = await fetch(`${API_URL}bookings/user/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, user_id: userId }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to fetch bookings'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data.bookings || []; +}; + +/** + * Update event status (admin only) + */ +export const updateEventStatus = async ( + username: string, + token: string, + eventId: number, + status: string +): Promise => { + const res = await fetch(`${API_URL}events/update-status/`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ username, token, event_id: eventId, status }), + }); + + const data = await res.json().catch(() => ({})); + + if (!res.ok) { + const errorMessage = data.message || data.errors || data.error || 'Failed to update event'; + const isTokenError = errorMessage.toLowerCase().includes('invalid token') || (res.status === 401 || res.status === 403); + throw new AuthError(errorMessage, isTokenError); + } + + return data; +}; diff --git a/src/types/partner.ts b/src/types/partner.ts new file mode 100644 index 0000000..61643e7 --- /dev/null +++ b/src/types/partner.ts @@ -0,0 +1,90 @@ +import { z } from 'zod'; + +export type PartnerType = 'Venue' | 'Promoter' | 'Sponsor' | 'Vendor' | 'Affiliate' | 'Other'; +export type PartnerStatus = 'Invited' | 'Active' | 'Suspended' | 'Archived'; +export type SettlementStatus = 'Open' | 'Processing' | 'Settled'; +export type VerificationStatus = 'Pending' | 'Verified' | 'Rejected'; + +export const PartnerSchema = z.object({ + id: z.string(), + name: z.string().min(2, "Name must be at least 2 characters"), + type: z.enum(['Venue', 'Promoter', 'Sponsor', 'Vendor', 'Affiliate', 'Other']), + status: z.enum(['Invited', 'Active', 'Suspended', 'Archived']), + logo: z.string().optional(), + primaryContact: z.object({ + name: z.string(), + email: z.string().email(), + phone: z.string().optional(), + role: z.string().optional(), + }), + companyDetails: z.object({ + legalName: z.string().optional(), + taxId: z.string().optional(), + website: z.string().url().optional(), + address: z.string().optional(), + }).optional(), + metrics: z.object({ + activeDeals: z.number().default(0), + totalRevenue: z.number().default(0), + openBalance: z.number().default(0), + lastActivity: z.string(), // ISO date + eventsCount: z.number().default(0), + }), + tags: z.array(z.string()).default([]), + notes: z.string().optional(), + joinedAt: z.string(), +}); + +export type Partner = z.infer; + +export const DealTermSchema = z.object({ + id: z.string(), + partnerId: z.string(), + type: z.enum(['RevenueShare', 'CommissionPerTicket', 'FixedFee', 'Tiered', 'Hybrid']), + name: z.string(), + params: z.object({ + percentage: z.number().optional(), // For RevenueShare + amount: z.number().optional(), // For FixedFee or Commission + currency: z.string().default('INR'), + tiers: z.array(z.object({ + threshold: z.number(), + percentage: z.number(), + })).optional(), + conditions: z.string().optional(), + }), + effectiveFrom: z.string(), + effectiveTo: z.string().optional(), + status: z.enum(['Draft', 'Active', 'Expired']), + version: z.number(), +}); + +export type DealTerm = z.infer; + +export const LedgerEntrySchema = z.object({ + id: z.string(), + partnerId: z.string(), + eventId: z.string().optional(), + type: z.enum(['Credit', 'Debit', 'Payout', 'Adjustment']), + description: z.string(), + amount: z.number(), + currency: z.string().default('INR'), + referenceId: z.string().optional(), // Invoice ID or Transaction ID + createdAt: z.string(), + status: z.enum(['Pending', 'Cleared', 'Failed']), +}); + +export type LedgerEntry = z.infer; + +export const PartnerDocumentSchema = z.object({ + id: z.string(), + partnerId: z.string(), + type: z.enum(['Contract', 'Invoice', 'Tax', 'Compliance', 'Other']), + name: z.string(), + url: z.string().url(), + status: z.enum(['Pending', 'Signed', 'Verified', 'Expired']), + uploadedBy: z.string(), + uploadedAt: z.string(), + expiresAt: z.string().optional(), +}); + +export type PartnerDocument = z.infer; diff --git a/vite.config.ts b/vite.config.ts index a9b992f..dccbc04 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,6 +11,15 @@ export default defineConfig(({ mode }) => ({ hmr: { overlay: false, }, + proxy: { + // Proxy API requests to bypass CORS during development + '/api': { + target: 'https://uat.eventifyplus.com', + changeOrigin: true, + secure: false, + rewrite: (path) => path, + }, + }, }, plugins: [react(), mode === "development" && componentTagger()].filter(Boolean), resolve: {