diff --git a/.gitignore b/.gitignore index a547bf3..9572296 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,8 @@ node_modules dist dist-ssr *.local +.env +.env.* # Editor directories and files .vscode/* diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..930283a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4614 @@ +{ + "name": "pezkuwi-p2p-mobile", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "pezkuwi-p2p-mobile", + "version": "1.0.0", + "dependencies": { + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-aspect-ratio": "^1.1.8", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-context-menu": "^2.2.16", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-hover-card": "^1.1.15", + "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-menubar": "^1.1.16", + "@radix-ui/react-navigation-menu": "^1.2.14", + "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.1.1", + "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slider": "^1.3.6", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-switch": "^1.1.0", + "@radix-ui/react-tabs": "^1.1.0", + "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/react-toggle": "^1.1.10", + "@radix-ui/react-toggle-group": "^1.1.11", + "@radix-ui/react-tooltip": "^1.1.4", + "@supabase/supabase-js": "^2.49.4", + "@tanstack/react-query": "^5.56.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^3.6.0", + "lucide-react": "^0.462.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "sonner": "^1.5.0", + "tailwind-merge": "^2.5.2", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@tailwindcss/typography": "^0.5.19", + "@types/node": "^25.1.0", + "@types/react": "^18.3.3", + "@types/react-dom": "^18.3.0", + "@vitejs/plugin-react-swc": "^3.5.0", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.47", + "tailwindcss": "^3.4.11", + "typescript": "^5.5.3", + "vite": "^7.3.1" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.4.tgz", + "integrity": "sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==", + "license": "MIT", + "dependencies": { + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.5.tgz", + "integrity": "sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==", + "license": "MIT", + "dependencies": { + "@floating-ui/core": "^1.7.4", + "@floating-ui/utils": "^0.2.10" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.7.tgz", + "integrity": "sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==", + "license": "MIT", + "dependencies": { + "@floating-ui/dom": "^1.7.5" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.10", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.10.tgz", + "integrity": "sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==", + "license": "MIT" + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@radix-ui/number": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", + "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==", + "license": "MIT" + }, + "node_modules/@radix-ui/primitive": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz", + "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==", + "license": "MIT" + }, + "node_modules/@radix-ui/react-accordion": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-accordion/-/react-accordion-1.2.12.tgz", + "integrity": "sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collapsible": "1.1.12", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-alert-dialog/-/react-alert-dialog-1.1.15.tgz", + "integrity": "sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dialog": "1.1.15", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-alert-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-arrow": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz", + "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-aspect-ratio": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-aspect-ratio/-/react-aspect-ratio-1.1.8.tgz", + "integrity": "sha512-5nZrJTF7gH+e0nZS7/QxFz6tJV4VimhQb1avEgtsJxvvIp5JilL+c58HICsKzPxghdwaDt48hEfPM1au4zGy+w==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-aspect-ratio/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.11.tgz", + "integrity": "sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-is-hydrated": "0.1.0", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-context": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", + "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-avatar/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-checkbox": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.3.3.tgz", + "integrity": "sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collapsible": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.12.tgz", + "integrity": "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz", + "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-collection/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-compose-refs": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz", + "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz", + "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-context-menu": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context-menu/-/react-context-menu-2.2.16.tgz", + "integrity": "sha512-O8morBEW+HsVG28gYDZPTrT9UUovQUlJue5YO836tiTJhuIWBm/zQHc7j388sHWtdH/xUZurK9olD2+pcqx5ww==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.15.tgz", + "integrity": "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-direction": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz", + "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dismissable-layer": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz", + "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-escape-keydown": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-dropdown-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.16.tgz", + "integrity": "sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.3.tgz", + "integrity": "sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz", + "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.15.tgz", + "integrity": "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-id": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz", + "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.8.tgz", + "integrity": "sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-label/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu": { + "version": "2.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.16.tgz", + "integrity": "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-menubar": { + "version": "1.1.16", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menubar/-/react-menubar-1.1.16.tgz", + "integrity": "sha512-EB1FktTz5xRRi2Er974AUQZWg2yVBb1yjip38/lgwtCVRd3a+maUoGHN/xs9Yv8SY8QwbSEb+YrxGadVWbEutA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-menu": "2.1.16", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-navigation-menu": { + "version": "1.2.14", + "resolved": "https://registry.npmjs.org/@radix-ui/react-navigation-menu/-/react-navigation-menu-1.2.14.tgz", + "integrity": "sha512-YB9mTFQvCOAQMHU+C/jVl96WmuWeltyUEpRJJky51huhds5W2FQr1J8D/16sQlf0ozxkPK8uF3niQMdUwZPv5w==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.15.tgz", + "integrity": "sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-popper": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz", + "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==", + "license": "MIT", + "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-rect": "1.1.1", + "@radix-ui/react-use-size": "1.1.1", + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-portal": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz", + "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-presence": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz", + "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz", + "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-primitive/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.8.tgz", + "integrity": "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.3", + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.3.tgz", + "integrity": "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-radio-group": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz", + "integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz", + "integrity": "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-scroll-area": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.10.tgz", + "integrity": "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.6.tgz", + "integrity": "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-focus-guards": "1.1.3", + "@radix-ui/react-focus-scope": "1.1.7", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.8.tgz", + "integrity": "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-separator/node_modules/@radix-ui/react-primitive": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.4.tgz", + "integrity": "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-slot": "1.2.4" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slider": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.6.tgz", + "integrity": "sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==", + "license": "MIT", + "dependencies": { + "@radix-ui/number": "1.1.1", + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-slot": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.4.tgz", + "integrity": "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-switch": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.6.tgz", + "integrity": "sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-previous": "1.1.1", + "@radix-ui/react-use-size": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tabs": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.13.tgz", + "integrity": "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toast": { + "version": "1.2.15", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.15.tgz", + "integrity": "sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-collection": "1.1.7", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-callback-ref": "1.1.1", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-use-layout-effect": "1.1.1", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle/-/react-toggle-1.1.10.tgz", + "integrity": "sha512-lS1odchhFTeZv3xwHH31YPObmJn8gOg7Lq12inrr0+BH/l3Tsq32VfjqH1oh80ARM3mlkfMic15n0kg4sD1poQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-toggle-group": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toggle-group/-/react-toggle-group-1.1.11.tgz", + "integrity": "sha512-5umnS0T8JQzQT6HbPyO7Hh9dgd82NmS36DQr+X/YJ9ctFNCiiQd6IJAYYZ33LUwm8M+taCz5t2ui29fHZc4Y6Q==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-direction": "1.1.1", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-roving-focus": "1.1.11", + "@radix-ui/react-toggle": "1.1.10", + "@radix-ui/react-use-controllable-state": "1.2.2" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz", + "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==", + "license": "MIT", + "dependencies": { + "@radix-ui/primitive": "1.1.3", + "@radix-ui/react-compose-refs": "1.1.2", + "@radix-ui/react-context": "1.1.2", + "@radix-ui/react-dismissable-layer": "1.1.11", + "@radix-ui/react-id": "1.1.1", + "@radix-ui/react-popper": "1.2.8", + "@radix-ui/react-portal": "1.1.9", + "@radix-ui/react-presence": "1.1.5", + "@radix-ui/react-primitive": "2.1.3", + "@radix-ui/react-slot": "1.2.3", + "@radix-ui/react-use-controllable-state": "1.2.2", + "@radix-ui/react-visually-hidden": "1.2.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz", + "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.2" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-callback-ref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz", + "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-controllable-state": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz", + "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-effect-event": "0.0.2", + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-effect-event": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz", + "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-escape-keydown": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz", + "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-callback-ref": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-is-hydrated": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-is-hydrated/-/react-use-is-hydrated-0.1.0.tgz", + "integrity": "sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.5.0" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-layout-effect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz", + "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-previous": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz", + "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz", + "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==", + "license": "MIT", + "dependencies": { + "@radix-ui/rect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-use-size": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz", + "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-use-layout-effect": "1.1.1" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@radix-ui/react-visually-hidden": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz", + "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-primitive": "2.1.3" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@radix-ui/rect": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz", + "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==", + "license": "MIT" + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.1.tgz", + "integrity": "sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.1.tgz", + "integrity": "sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.1.tgz", + "integrity": "sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.1.tgz", + "integrity": "sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.1.tgz", + "integrity": "sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.1.tgz", + "integrity": "sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.1.tgz", + "integrity": "sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.1.tgz", + "integrity": "sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.1.tgz", + "integrity": "sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.1.tgz", + "integrity": "sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.1.tgz", + "integrity": "sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.1.tgz", + "integrity": "sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.1.tgz", + "integrity": "sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.1.tgz", + "integrity": "sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.1.tgz", + "integrity": "sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.1.tgz", + "integrity": "sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.1.tgz", + "integrity": "sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.1.tgz", + "integrity": "sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.1.tgz", + "integrity": "sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.1.tgz", + "integrity": "sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.1.tgz", + "integrity": "sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.1.tgz", + "integrity": "sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.1.tgz", + "integrity": "sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.1.tgz", + "integrity": "sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.1.tgz", + "integrity": "sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@supabase/auth-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/auth-js/-/auth-js-2.93.3.tgz", + "integrity": "sha512-JdnkHZPKexVGSNONtu89RHU4bxz3X9kxx+f5ZnR5osoCIX+vs/MckwWRPZEybAEvlJXt5xjomDb3IB876QCxWQ==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/functions-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/functions-js/-/functions-js-2.93.3.tgz", + "integrity": "sha512-qWO0gHNDm/5jRjROv/nv9L6sYabCWS1kzorOLUv3kqCwRvEJLYZga93ppJPrZwOgoZfXmJzvpjY8fODA4HQfBw==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/postgrest-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/postgrest-js/-/postgrest-js-2.93.3.tgz", + "integrity": "sha512-+iJ96g94skO2e4clsRSmEXg22NUOjh9BziapsJSAvnB1grOBf/BA8vGtCHjNOA+Z6lvKXL1jwBqcL9+fS1W/Lg==", + "license": "MIT", + "dependencies": { + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/realtime-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/realtime-js/-/realtime-js-2.93.3.tgz", + "integrity": "sha512-gnYpcFzwy8IkezRP4CDbT5I8jOsiOjrWrqTY1B+7jIriXsnpifmlM6RRjLBm9oD7OwPG0/WksniGPdKW67sXOA==", + "license": "MIT", + "dependencies": { + "@types/phoenix": "^1.6.6", + "@types/ws": "^8.18.1", + "tslib": "2.8.1", + "ws": "^8.18.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/storage-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/storage-js/-/storage-js-2.93.3.tgz", + "integrity": "sha512-cw4qXiLrx3apglDM02Tx/w/stvFlrkKocC6vCvuFAz3JtVEl1zH8MUfDQDTH59kJAQVaVdbewrMWSoBob7REnA==", + "license": "MIT", + "dependencies": { + "iceberg-js": "^0.8.1", + "tslib": "2.8.1" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@supabase/supabase-js": { + "version": "2.93.3", + "resolved": "https://registry.npmjs.org/@supabase/supabase-js/-/supabase-js-2.93.3.tgz", + "integrity": "sha512-paUqEqdBI9ztr/4bbMoCgeJ6M8ZTm2fpfjSOlzarPuzYveKFM20ZfDZqUpi9CFfYagYj5Iv3m3ztUjaI9/tM1w==", + "license": "MIT", + "dependencies": { + "@supabase/auth-js": "2.93.3", + "@supabase/functions-js": "2.93.3", + "@supabase/postgrest-js": "2.93.3", + "@supabase/realtime-js": "2.93.3", + "@supabase/storage-js": "2.93.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@swc/core": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.11.tgz", + "integrity": "sha512-iLmLTodbYxU39HhMPaMUooPwO/zqJWvsqkrXv1ZI38rMb048p6N7qtAtTp37sw9NzSrvH6oli8EdDygo09IZ/w==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.25" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.15.11", + "@swc/core-darwin-x64": "1.15.11", + "@swc/core-linux-arm-gnueabihf": "1.15.11", + "@swc/core-linux-arm64-gnu": "1.15.11", + "@swc/core-linux-arm64-musl": "1.15.11", + "@swc/core-linux-x64-gnu": "1.15.11", + "@swc/core-linux-x64-musl": "1.15.11", + "@swc/core-win32-arm64-msvc": "1.15.11", + "@swc/core-win32-ia32-msvc": "1.15.11", + "@swc/core-win32-x64-msvc": "1.15.11" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.11.tgz", + "integrity": "sha512-QoIupRWVH8AF1TgxYyeA5nS18dtqMuxNwchjBIwJo3RdwLEFiJq6onOx9JAxHtuPwUkIVuU2Xbp+jCJ7Vzmgtg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.11.tgz", + "integrity": "sha512-S52Gu1QtPSfBYDiejlcfp9GlN+NjTZBRRNsz8PNwBgSE626/FUf2PcllVUix7jqkoMC+t0rS8t+2/aSWlMuQtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.11.tgz", + "integrity": "sha512-lXJs8oXo6Z4yCpimpQ8vPeCjkgoHu5NoMvmJZ8qxDyU99KVdg6KwU9H79vzrmB+HfH+dCZ7JGMqMF//f8Cfvdg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.11.tgz", + "integrity": "sha512-chRsz1K52/vj8Mfq/QOugVphlKPWlMh10V99qfH41hbGvwAU6xSPd681upO4bKiOr9+mRIZZW+EfJqY42ZzRyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.11.tgz", + "integrity": "sha512-PYftgsTaGnfDK4m6/dty9ryK1FbLk+LosDJ/RJR2nkXGc8rd+WenXIlvHjWULiBVnS1RsjHHOXmTS4nDhe0v0w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.11.tgz", + "integrity": "sha512-DKtnJKIHiZdARyTKiX7zdRjiDS1KihkQWatQiCHMv+zc2sfwb4Glrodx2VLOX4rsa92NLR0Sw8WLcPEMFY1szQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.11.tgz", + "integrity": "sha512-mUjjntHj4+8WBaiDe5UwRNHuEzLjIWBTSGTw0JT9+C9/Yyuh4KQqlcEQ3ro6GkHmBGXBFpGIj/o5VMyRWfVfWw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.11.tgz", + "integrity": "sha512-ZkNNG5zL49YpaFzfl6fskNOSxtcZ5uOYmWBkY4wVAvgbSAQzLRVBp+xArGWh2oXlY/WgL99zQSGTv7RI5E6nzA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.11.tgz", + "integrity": "sha512-6XnzORkZCQzvTQ6cPrU7iaT9+i145oLwnin8JrfsLG41wl26+5cNQ2XV3zcbrnFEV6esjOceom9YO1w9mGJByw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.15.11", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.11.tgz", + "integrity": "sha512-IQ2n6af7XKLL6P1gIeZACskSxK8jWtoKpJWLZmdXTDj1MGzktUy4i+FvpdtxFmJWNavRWH1VmTr6kAubRDHeKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.25", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.25.tgz", + "integrity": "sha512-iAoY/qRhNH8a/hBvm3zKj9qQ4oc2+3w1unPJa2XvTK3XjeLXtzcCingVPw/9e5mn1+0yPqxcBGp9Jf0pkfMb1g==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tanstack/query-core": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.90.20.tgz", + "integrity": "sha512-OMD2HLpNouXEfZJWcKeVKUgQ5n+n3A2JFmBaScpNDUqSrQSjiveC7dKMe53uJUg1nDG16ttFPz2xfilz6i2uVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.90.20", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.90.20.tgz", + "integrity": "sha512-vXBxa+qeyveVO7OA0jX1z+DeyCA4JKnThKv411jd5SORpBKgkcVnYKCiBgECvADvniBX7tobwBmg01qq9JmMJw==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.90.20" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.1.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.1.0.tgz", + "integrity": "sha512-t7frlewr6+cbx+9Ohpl0NOTKXZNV9xHRmNOvql47BFJKcEG1CxtxlPEEe+gR9uhVWM4DwhnvTF110mIL4yP9RA==", + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/phoenix": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@types/phoenix/-/phoenix-1.6.7.tgz", + "integrity": "sha512-oN9ive//QSBkf19rfDv45M7eZPi0eEXylht2OLEXicu5b4KoQ1OzXIw+xDSGWxSxe1JmepRR/ZH283vsu518/Q==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/prop-types": "*", + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", + "devOptional": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.11.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.11.0.tgz", + "integrity": "sha512-YTJCGFdNMHCMfjODYtxRNVAYmTWQ1Lb8PulP/2/f/oEEtglw8oKxKIZmmRkyXrVrHfsKOaVkAc3NT9/dMutO5w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-beta.27", + "@swc/core": "^1.12.11" + }, + "peerDependencies": { + "vite": "^4 || ^5 || ^6 || ^7" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "license": "MIT" + }, + "node_modules/aria-hidden": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz", + "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/autoprefixer": { + "version": "10.4.24", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.24.tgz", + "integrity": "sha512-uHZg7N9ULTVbutaIsDRoUkoS8/h3bdsmVJYZ5l3wv8Cp/6UIIoRDm90hZ+BwxUj/hGBEzLxdHNSKuFpn8WOyZw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.1", + "caniuse-lite": "^1.0.30001766", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.19", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.19.tgz", + "integrity": "sha512-ipDqC8FrAl/76p2SSWKSI+H9tFwm7vYqXQrItCuiVPt26Km0jS+NzSsBWAaBusvSbQcfJG+JitdMm+wZAgTYqg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz", + "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "devOptional": true, + "license": "MIT" + }, + "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", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/kossnocorp" + } + }, + "node_modules/detect-node-es": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.283", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.283.tgz", + "integrity": "sha512-3vifjt1HgrGW/h76UEeny+adYApveS9dH2h3p57JYzBSXJIKUJAvtmIytDKjcSCt9xHfrNCFJ7gts6vkhuq++w==", + "dev": true, + "license": "ISC" + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-nonce": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", + "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/iceberg-js": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", + "integrity": "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "node_modules/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "node_modules/lucide-react": { + "version": "0.462.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.462.0.tgz", + "integrity": "sha512-NTL7EbAao9IFtuSivSZgrAh4fZd09Lr+6MTkqIxuHaH2nnYiYIzXPo06cOxHg9wKLdj6LL8TByG4qpePqwgx/g==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-nested/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "license": "MIT" + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-remove-scroll": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.2.tgz", + "integrity": "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==", + "license": "MIT", + "dependencies": { + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", + "tslib": "^2.1.0", + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-remove-scroll-bar": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", + "dependencies": { + "react-style-singleton": "^2.2.2", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/react-style-singleton": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", + "dependencies": { + "get-nonce": "^1.0.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.57.1", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.1.tgz", + "integrity": "sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.1", + "@rollup/rollup-android-arm64": "4.57.1", + "@rollup/rollup-darwin-arm64": "4.57.1", + "@rollup/rollup-darwin-x64": "4.57.1", + "@rollup/rollup-freebsd-arm64": "4.57.1", + "@rollup/rollup-freebsd-x64": "4.57.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.1", + "@rollup/rollup-linux-arm-musleabihf": "4.57.1", + "@rollup/rollup-linux-arm64-gnu": "4.57.1", + "@rollup/rollup-linux-arm64-musl": "4.57.1", + "@rollup/rollup-linux-loong64-gnu": "4.57.1", + "@rollup/rollup-linux-loong64-musl": "4.57.1", + "@rollup/rollup-linux-ppc64-gnu": "4.57.1", + "@rollup/rollup-linux-ppc64-musl": "4.57.1", + "@rollup/rollup-linux-riscv64-gnu": "4.57.1", + "@rollup/rollup-linux-riscv64-musl": "4.57.1", + "@rollup/rollup-linux-s390x-gnu": "4.57.1", + "@rollup/rollup-linux-x64-gnu": "4.57.1", + "@rollup/rollup-linux-x64-musl": "4.57.1", + "@rollup/rollup-openbsd-x64": "4.57.1", + "@rollup/rollup-openharmony-arm64": "4.57.1", + "@rollup/rollup-win32-arm64-msvc": "4.57.1", + "@rollup/rollup-win32-ia32-msvc": "4.57.1", + "@rollup/rollup-win32-x64-gnu": "4.57.1", + "@rollup/rollup-win32-x64-msvc": "4.57.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "node_modules/sonner": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/sonner/-/sonner-1.7.4.tgz", + "integrity": "sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==", + "license": "MIT", + "peerDependencies": { + "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz", + "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tailwindcss-animate": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/tailwindcss-animate/-/tailwindcss-animate-1.0.7.tgz", + "integrity": "sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==", + "license": "MIT", + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/tailwindcss/node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "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==", + "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==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/use-callback-ref": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", + "dependencies": { + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sidecar": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", + "dependencies": { + "detect-node-es": "^1.1.0", + "tslib": "^2.0.0" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/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/vite/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/ws": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", + "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", + "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 + } + } + } + } +} diff --git a/package.json b/package.json index 005f1a0..e7e59df 100644 --- a/package.json +++ b/package.json @@ -10,20 +10,32 @@ "lint": "eslint ." }, "dependencies": { - "@pezkuwi/api": "^16.5.18", - "@pezkuwi/keyring": "^14.0.13", - "@pezkuwi/util": "^14.0.13", - "@pezkuwi/util-crypto": "^14.0.13", + "@radix-ui/react-accordion": "^1.2.12", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-aspect-ratio": "^1.1.8", + "@radix-ui/react-avatar": "^1.1.11", + "@radix-ui/react-checkbox": "^1.3.3", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-context-menu": "^2.2.16", "@radix-ui/react-dialog": "^1.1.2", "@radix-ui/react-dropdown-menu": "^2.1.1", + "@radix-ui/react-hover-card": "^1.1.15", "@radix-ui/react-label": "^2.1.0", + "@radix-ui/react-menubar": "^1.1.16", + "@radix-ui/react-navigation-menu": "^1.2.14", "@radix-ui/react-popover": "^1.1.1", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-radio-group": "^1.3.8", + "@radix-ui/react-scroll-area": "^1.2.10", "@radix-ui/react-select": "^2.1.1", "@radix-ui/react-separator": "^1.1.0", + "@radix-ui/react-slider": "^1.3.6", "@radix-ui/react-slot": "^1.1.0", "@radix-ui/react-switch": "^1.1.0", "@radix-ui/react-tabs": "^1.1.0", "@radix-ui/react-toast": "^1.2.1", + "@radix-ui/react-toggle": "^1.1.10", + "@radix-ui/react-toggle-group": "^1.1.11", "@radix-ui/react-tooltip": "^1.1.4", "@supabase/supabase-js": "^2.49.4", "@tanstack/react-query": "^5.56.2", @@ -33,12 +45,13 @@ "lucide-react": "^0.462.0", "react": "^18.3.1", "react-dom": "^18.3.1", - "react-router-dom": "^6.26.2", "sonner": "^1.5.0", "tailwind-merge": "^2.5.2", "tailwindcss-animate": "^1.0.7" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.19", + "@types/node": "^25.1.0", "@types/react": "^18.3.3", "@types/react-dom": "^18.3.0", "@vitejs/plugin-react-swc": "^3.5.0", diff --git a/src/components/p2p/AdList.tsx b/src/components/p2p/AdList.tsx index 249f852..fc18c0b 100644 --- a/src/components/p2p/AdList.tsx +++ b/src/components/p2p/AdList.tsx @@ -7,7 +7,7 @@ import { Loader2, Shield, Zap } from 'lucide-react'; import { useAuth } from '@/contexts/AuthContext'; import { TradeModal } from './TradeModal'; import { MerchantTierBadge } from './MerchantTierBadge'; -import { getUserReputation, type P2PFiatOffer, type P2PReputation } from '@shared/lib/p2p-fiat'; +import { getUserReputation, type P2PFiatOffer, type P2PReputation } from '@/lib/p2p-fiat'; import { supabase } from '@/lib/supabase'; import type { P2PFilters } from './types'; diff --git a/src/components/p2p/CreateAd.tsx b/src/components/p2p/CreateAd.tsx index 2c4545f..513bfee 100644 --- a/src/components/p2p/CreateAd.tsx +++ b/src/components/p2p/CreateAd.tsx @@ -15,7 +15,7 @@ import { type PaymentMethod, type FiatCurrency, type CryptoToken -} from '@shared/lib/p2p-fiat'; +} from '@/lib/p2p-fiat'; interface CreateAdProps { onAdCreated: () => void; diff --git a/src/components/p2p/DepositModal.tsx b/src/components/p2p/DepositModal.tsx index 347465c..7db61af 100644 --- a/src/components/p2p/DepositModal.tsx +++ b/src/components/p2p/DepositModal.tsx @@ -1,3 +1,10 @@ +/** + * Deposit Modal - Mobile P2P + * + * Shows platform wallet address for user to send tokens. + * After sending, user enters tx hash to verify deposit. + * Actual verification happens in backend Edge Function. + */ import { useState, useEffect } from 'react'; import { Dialog, @@ -18,24 +25,17 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Skeleton } from '@/components/ui/skeleton'; import { Loader2, Copy, CheckCircle2, AlertTriangle, ExternalLink, - QrCode, Wallet } from 'lucide-react'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; -import { useWallet } from '@/contexts/WalletContext'; import { toast } from 'sonner'; import { supabase } from '@/lib/supabase'; -import { - getPlatformWalletAddress, - type CryptoToken -} from '@shared/lib/p2p-fiat'; +import { getPlatformWalletAddress, type CryptoToken } from '@/lib/p2p-fiat'; interface DepositModalProps { isOpen: boolean; @@ -43,22 +43,17 @@ interface DepositModalProps { onSuccess?: () => void; } -type DepositStep = 'select' | 'send' | 'verify' | 'success'; +type DepositStep = 'info' | 'verify' | 'success'; export function DepositModal({ isOpen, onClose, onSuccess }: DepositModalProps) { - const { api, selectedAccount } = usePezkuwi(); - const { balances, signTransaction } = useWallet(); - - const [step, setStep] = useState('select'); + const [step, setStep] = useState('info'); const [token, setToken] = useState('HEZ'); const [amount, setAmount] = useState(''); const [platformWallet, setPlatformWallet] = useState(''); const [txHash, setTxHash] = useState(''); - const [loading, setLoading] = useState(false); const [copied, setCopied] = useState(false); const [verifying, setVerifying] = useState(false); - // Fetch platform wallet address on mount useEffect(() => { if (isOpen) { fetchPlatformWallet(); @@ -71,11 +66,10 @@ export function DepositModal({ isOpen, onClose, onSuccess }: DepositModalProps) }; const resetModal = () => { - setStep('select'); + setStep('info'); setToken('HEZ'); setAmount(''); setTxHash(''); - setLoading(false); setCopied(false); setVerifying(false); }; @@ -89,64 +83,11 @@ export function DepositModal({ isOpen, onClose, onSuccess }: DepositModalProps) try { await navigator.clipboard.writeText(platformWallet); setCopied(true); - toast.success('Address copied to clipboard'); + toast.success('Address copied!'); + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); setTimeout(() => setCopied(false), 2000); } catch { - toast.error('Failed to copy address'); - } - }; - - const getAvailableBalance = () => { - if (token === 'HEZ') return balances.HEZ; - if (token === 'PEZ') return balances.PEZ; - return '0'; - }; - - const handleSendDeposit = async () => { - if (!api || !selectedAccount) { - toast.error('Please connect your wallet'); - return; - } - - const depositAmount = parseFloat(amount); - if (isNaN(depositAmount) || depositAmount <= 0) { - toast.error('Please enter a valid amount'); - return; - } - - setLoading(true); - - try { - // Build the transfer transaction - const DECIMALS = 12; - const amountBN = BigInt(Math.floor(depositAmount * 10 ** DECIMALS)); - - let tx; - if (token === 'HEZ') { - // Native transfer - tx = api.tx.balances.transferKeepAlive(platformWallet, amountBN); - } else { - // Asset transfer (PEZ = asset ID 1) - const assetId = token === 'PEZ' ? 1 : 0; - tx = api.tx.assets.transfer(assetId, platformWallet, amountBN); - } - - toast.info('Please sign the transaction in your wallet...'); - - // Sign and send - const hash = await signTransaction(tx); - - if (hash) { - setTxHash(hash); - setStep('verify'); - toast.success('Transaction sent! Please verify your deposit.'); - } - } catch (error: unknown) { - console.error('Deposit transaction error:', error); - const message = error instanceof Error ? error.message : 'Transaction failed'; - toast.error(message); - } finally { - setLoading(false); + toast.error('Failed to copy'); } }; @@ -158,50 +99,56 @@ export function DepositModal({ isOpen, onClose, onSuccess }: DepositModalProps) const depositAmount = parseFloat(amount); if (isNaN(depositAmount) || depositAmount <= 0) { - toast.error('Invalid amount'); + toast.error('Please enter the deposit amount'); return; } setVerifying(true); try { - // Call the Edge Function for secure deposit verification - // This verifies the transaction on-chain before crediting balance const { data, error } = await supabase.functions.invoke('verify-deposit', { - body: { - txHash, - token, - expectedAmount: depositAmount - } + body: { txHash, token, expectedAmount: depositAmount } }); - if (error) { - throw new Error(error.message || 'Verification failed'); - } + if (error) throw new Error(error.message || 'Verification failed'); if (data?.success) { - toast.success(`Deposit verified! ${data.amount} ${token} added to your balance.`); + toast.success(`Deposit verified! ${data.amount} ${token} added.`); + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); setStep('success'); onSuccess?.(); } else { throw new Error(data?.error || 'Verification failed'); } } catch (error) { - console.error('Verify deposit error:', error); const message = error instanceof Error ? error.message : 'Verification failed'; toast.error(message); + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('error'); } finally { setVerifying(false); } }; - const renderStepContent = () => { - switch (step) { - case 'select': - return ( -
+ return ( + !open && handleClose()}> + + + + + Deposit + + {step !== 'success' && ( + + {step === 'info' && 'Send tokens to the platform wallet'} + {step === 'verify' && 'Enter transaction hash to verify'} + + )} + + + {step === 'info' && ( +
- + setAmount(e.target.value)} - min="0" - step="0.0001" - /> -
- {token} -
-
-

- Wallet Balance: {parseFloat(getAvailableBalance()).toFixed(4)} {token} -

+ + setAmount(e.target.value)} + min="0" + step="0.0001" + />
- - - - You will send {token} from your connected wallet to the P2P platform escrow. - After confirmation, the amount will be credited to your P2P internal balance. - - - - - - - -
- ); - - case 'send': - return ( -
-
-
-
- -
-

Send {amount} {token} to:

+
+

Send {token} to:

+
+ {platformWallet || 'Loading...'}
- - {platformWallet ? ( -
-
- {platformWallet} -
- -
- ) : ( - - )} +
- - Only send {token} on the PezkuwiChain network. Sending other tokens or using - other networks will result in permanent loss of funds. + + Only send {token} on PezkuwiChain. Wrong network = lost funds. -
- -
+
- ); - - case 'verify': - return ( -
- - - - Transaction sent! Please verify your deposit to credit your P2P balance. - - + )} + {step === 'verify' && ( +
@@ -361,89 +236,50 @@ export function DepositModal({ isOpen, onClose, onSuccess }: DepositModalProps)
-
-
-
-

Token

-

{token}

-
-
-

Amount

-

{amount}

-
+
+
+ Token + {token} +
+
+ Amount + {amount}
- -
- ); + )} - case 'success': - return ( -
-
- + {step === 'success' && ( +
+
+
-
-

- Deposit Successful! -

-

- {amount} {token} has been added to your P2P internal balance. +

Deposit Successful!

+

+ {amount} {token} added to your P2P balance.

- -
-

- You can now create sell offers or trade P2P using your internal balance. - No blockchain fees during P2P trades! -

-
- - +
- ); - } - }; - - return ( - !open && handleClose()}> - - - - - Deposit to P2P Balance - - {step !== 'success' && ( - - {step === 'select' && 'Deposit crypto from your wallet to P2P internal balance'} - {step === 'send' && 'Send tokens to the platform escrow wallet'} - {step === 'verify' && 'Verify your transaction to credit your balance'} - - )} - - - {renderStepContent()} + )} ); diff --git a/src/components/p2p/ExpressMode.tsx b/src/components/p2p/ExpressMode.tsx index 0cc1993..6787a38 100644 --- a/src/components/p2p/ExpressMode.tsx +++ b/src/components/p2p/ExpressMode.tsx @@ -17,9 +17,8 @@ import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Zap, ArrowRight, Shield, Clock, Star, AlertCircle, CheckCircle2 } from 'lucide-react'; import { supabase } from '@/lib/supabase'; import { useAuth } from '@/contexts/AuthContext'; -import { useNavigate } from 'react-router-dom'; import { toast } from 'sonner'; -import type { CryptoToken, FiatCurrency } from '@pezkuwi/lib/p2p-fiat'; +import type { CryptoToken, FiatCurrency } from '@/lib/p2p-fiat'; interface BestOffer { id: string; @@ -68,7 +67,6 @@ export function ExpressMode({ onTradeStarted }: ExpressModeProps) { const [isProcessing, setIsProcessing] = useState(false); const { user } = useAuth(); - const navigate = useNavigate(); // Calculate conversion const fiatSymbol = SUPPORTED_FIATS.find(f => f.code === fiat)?.symbol || ''; @@ -172,12 +170,7 @@ export function ExpressMode({ onTradeStarted }: ExpressModeProps) { } toast.success('Express trade started!'); - - if (onTradeStarted) { - onTradeStarted(response.trade_id); - } else { - navigate(`/p2p/trade/${response.trade_id}`); - } + onTradeStarted?.(response.trade_id); } catch (err) { console.error('Express trade error:', err); toast.error(err instanceof Error ? err.message : 'Failed to start trade'); diff --git a/src/components/p2p/InternalBalanceCard.tsx b/src/components/p2p/InternalBalanceCard.tsx index 53a4a4d..eac1994 100644 --- a/src/components/p2p/InternalBalanceCard.tsx +++ b/src/components/p2p/InternalBalanceCard.tsx @@ -11,7 +11,7 @@ import { Lock, Unlock } from 'lucide-react'; -import { getInternalBalances, type InternalBalance } from '@shared/lib/p2p-fiat'; +import { getInternalBalances, type InternalBalance } from '@/lib/p2p-fiat'; interface InternalBalanceCardProps { onDeposit?: () => void; diff --git a/src/components/p2p/MyTrades.tsx b/src/components/p2p/MyTrades.tsx new file mode 100644 index 0000000..7fe6215 --- /dev/null +++ b/src/components/p2p/MyTrades.tsx @@ -0,0 +1,95 @@ +import { useState, useEffect } from 'react'; +import { Card, CardContent } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { Clock, CheckCircle2, XCircle, AlertTriangle } from 'lucide-react'; +import { getUserTrades, type P2PFiatTrade } from '@/lib/p2p-fiat'; +import { useAuth } from '@/contexts/AuthContext'; +import { formatDistanceToNow } from 'date-fns'; + +interface MyTradesProps { + onTradeSelect: (tradeId: string) => void; +} + +export function MyTrades({ onTradeSelect }: MyTradesProps) { + const [trades, setTrades] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const { user } = useAuth(); + + useEffect(() => { + const fetchTrades = async () => { + if (!user) return; + setIsLoading(true); + try { + const data = await getUserTrades(user.id); + setTrades(data); + } catch (error) { + console.error('Fetch trades error:', error); + } finally { + setIsLoading(false); + } + }; + fetchTrades(); + }, [user]); + + const getStatusBadge = (status: string) => { + switch (status) { + case 'pending': + return Pending; + case 'payment_sent': + return Payment Sent; + case 'completed': + return Completed; + case 'cancelled': + return Cancelled; + case 'disputed': + return Disputed; + default: + return {status}; + } + }; + + if (isLoading) { + return ( +
+
+
+ ); + } + + if (trades.length === 0) { + return ( +
+

No trades yet

+

Start by accepting an offer

+
+ ); + } + + return ( +
+ {trades.map((trade) => ( + onTradeSelect(trade.id)} + > + +
+ + {trade.crypto_amount.toFixed(4)} HEZ + + {getStatusBadge(trade.status)} +
+
+ ${trade.fiat_amount.toFixed(2)} + {formatDistanceToNow(new Date(trade.created_at), { addSuffix: true })} +
+
+ {user?.id === trade.seller_id ? 'Selling' : 'Buying'} +
+
+
+ ))} +
+ ); +} diff --git a/src/components/p2p/NotificationBell.tsx b/src/components/p2p/NotificationBell.tsx index 1a344f7..eafb8ef 100644 --- a/src/components/p2p/NotificationBell.tsx +++ b/src/components/p2p/NotificationBell.tsx @@ -1,5 +1,4 @@ -import React, { useState, useEffect, useCallback } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useState, useEffect, useCallback } from 'react'; import { Button } from '@/components/ui/button'; import { DropdownMenu, @@ -37,14 +36,12 @@ interface Notification { } export function NotificationBell() { - const navigate = useNavigate(); const { user } = useAuth(); const [notifications, setNotifications] = useState([]); const [unreadCount, setUnreadCount] = useState(0); const [loading, setLoading] = useState(true); const [isOpen, setIsOpen] = useState(false); - // Fetch notifications const fetchNotifications = useCallback(async () => { if (!user) return; @@ -67,12 +64,10 @@ export function NotificationBell() { } }, [user]); - // Initial fetch useEffect(() => { fetchNotifications(); }, [fetchNotifications]); - // Real-time subscription useEffect(() => { if (!user) return; @@ -90,6 +85,8 @@ export function NotificationBell() { const newNotif = payload.new as Notification; setNotifications(prev => [newNotif, ...prev.slice(0, 19)]); setUnreadCount(prev => prev + 1); + // Haptic feedback + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); } ) .subscribe(); @@ -99,7 +96,6 @@ export function NotificationBell() { }; }, [user]); - // Mark as read const markAsRead = async (notificationId: string) => { try { await supabase @@ -116,7 +112,6 @@ export function NotificationBell() { } }; - // Mark all as read const markAllAsRead = async () => { if (!user) return; @@ -134,21 +129,13 @@ export function NotificationBell() { } }; - // Handle notification click const handleClick = (notification: Notification) => { - // Mark as read if (!notification.is_read) { markAsRead(notification.id); } - - // Navigate to reference - if (notification.reference_type === 'trade' && notification.reference_id) { - navigate(`/p2p/trade/${notification.reference_id}`); - setIsOpen(false); - } + setIsOpen(false); }; - // Get icon for notification type const getIcon = (type: string) => { switch (type) { case 'new_message': @@ -170,7 +157,6 @@ export function NotificationBell() { } }; - // Format time ago const formatTimeAgo = (dateString: string) => { const seconds = Math.floor((Date.now() - new Date(dateString).getTime()) / 1000); if (seconds < 60) return 'Just now'; @@ -187,11 +173,7 @@ export function NotificationBell() { return ( - - + - Notifications + Notifications {unreadCount > 0 && ( )} - + {loading ? (
- +
) : notifications.length === 0 ? ( -
+

No notifications

@@ -236,23 +215,19 @@ export function NotificationBell() { handleClick(notification)} - className={` - flex items-start gap-3 p-3 cursor-pointer - ${!notification.is_read ? 'bg-gray-800/50' : ''} - hover:bg-gray-800 - `} + className={`flex items-start gap-3 p-3 cursor-pointer ${!notification.is_read ? 'bg-accent/50' : ''}`} >
{getIcon(notification.type)}
-

+

{notification.title}

{notification.message && ( -

+

{notification.message}

)} -

+

{formatTimeAgo(notification.created_at)}

@@ -263,21 +238,6 @@ export function NotificationBell() { )) )} - - {notifications.length > 0 && ( - <> - - { - navigate('/p2p/orders'); - setIsOpen(false); - }} - className="justify-center text-gray-400 hover:text-white cursor-pointer" - > - View all trades - - - )} ); diff --git a/src/components/p2p/P2PDashboard.tsx b/src/components/p2p/P2PDashboard.tsx index 2be18a8..89ea66c 100644 --- a/src/components/p2p/P2PDashboard.tsx +++ b/src/components/p2p/P2PDashboard.tsx @@ -1,10 +1,12 @@ -import React, { useState, useEffect } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useState, useEffect } from 'react'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Button } from '@/components/ui/button'; import { Badge } from '@/components/ui/badge'; import { Card, CardContent } from '@/components/ui/card'; -import { PlusCircle, Home, ClipboardList, TrendingUp, CheckCircle2, Clock, Store, Zap, Blocks } from 'lucide-react'; +import { + PlusCircle, ClipboardList, TrendingUp, CheckCircle2, Clock, + ArrowLeft, Zap, Blocks, Wallet, ArrowDownToLine, ArrowUpFromLine +} from 'lucide-react'; import { AdList } from './AdList'; import { CreateAd } from './CreateAd'; import { NotificationBell } from './NotificationBell'; @@ -14,10 +16,14 @@ import { DepositModal } from './DepositModal'; import { WithdrawModal } from './WithdrawModal'; import { ExpressMode } from './ExpressMode'; import { BlockTrade } from './BlockTrade'; +import { MyTrades } from './MyTrades'; +import { TradeDetail } from './TradeDetail'; import { DEFAULT_FILTERS, type P2PFilters } from './types'; import { useAuth } from '@/contexts/AuthContext'; import { supabase } from '@/lib/supabase'; +type View = 'main' | 'create-ad' | 'my-trades' | 'trade-detail'; + interface UserStats { activeTrades: number; completedTrades: number; @@ -25,40 +31,42 @@ interface UserStats { } export function P2PDashboard() { - const [showCreateAd, setShowCreateAd] = useState(false); + const [view, setView] = useState('main'); + const [selectedTradeId, setSelectedTradeId] = useState(null); const [userStats, setUserStats] = useState({ activeTrades: 0, completedTrades: 0, totalVolume: 0 }); const [filters, setFilters] = useState(DEFAULT_FILTERS); const [showDepositModal, setShowDepositModal] = useState(false); const [showWithdrawModal, setShowWithdrawModal] = useState(false); const [balanceRefreshKey, setBalanceRefreshKey] = useState(0); - const navigate = useNavigate(); - const { user } = useAuth(); + const { user, isLoading, error, login } = useAuth(); const handleBalanceUpdated = () => { setBalanceRefreshKey(prev => prev + 1); }; + const handleTradeStarted = (tradeId: string) => { + setSelectedTradeId(tradeId); + setView('trade-detail'); + }; + // Fetch user stats useEffect(() => { const fetchStats = async () => { if (!user) return; try { - // Count active trades const { count: activeCount } = await supabase .from('p2p_fiat_trades') .select('*', { count: 'exact', head: true }) .or(`seller_id.eq.${user.id},buyer_id.eq.${user.id}`) .in('status', ['pending', 'payment_sent']); - // Count completed trades const { count: completedCount } = await supabase .from('p2p_fiat_trades') .select('*', { count: 'exact', head: true }) .or(`seller_id.eq.${user.id},buyer_id.eq.${user.id}`) .eq('status', 'completed'); - // Calculate total volume const { data: trades } = await supabase .from('p2p_fiat_trades') .select('fiat_amount') @@ -72,219 +80,229 @@ export function P2PDashboard() { completedTrades: completedCount || 0, totalVolume, }); - } catch (error) { - console.error('Fetch stats error:', error); + } catch (err) { + console.error('Fetch stats error:', err); } }; fetchStats(); }, [user]); + // Loading state + if (isLoading) { + return ( +
+
+
+

Connecting...

+
+
+ ); + } + + // Not authenticated + if (!user) { + return ( +
+
+ +

P2P Trading

+

+ Trade crypto with local currency securely via Telegram. +

+ {error && ( +

{error}

+ )} + +
+
+ ); + } + + // Trade detail view + if (view === 'trade-detail' && selectedTradeId) { + return ( +
+
+ +
+ +
+ ); + } + + // My trades view + if (view === 'my-trades') { + return ( +
+
+ +

My Trades

+
+
+ { + setSelectedTradeId(id); + setView('trade-detail'); + }} /> +
+ ); + } + + // Create ad view + if (view === 'create-ad') { + return ( +
+
+ +

Create Ad

+
+
+ setView('main')} /> +
+ ); + } + + // Main dashboard view return ( -
-
- -
- +
+ {/* Header */} +
+
+

P2P Trading

+
+ + +
+
+
+ +
+ {/* Balance Card */} + setShowDepositModal(true)} + onWithdraw={() => setShowWithdrawModal(true)} + /> + + {/* Quick Stats */} +
+ + + +

{userStats.activeTrades}

+

Active

+
+
+ + + +

{userStats.completedTrades}

+

Done

+
+
+ + + +

${userStats.totalVolume > 1000 ? `${(userStats.totalVolume / 1000).toFixed(1)}K` : userStats.totalVolume}

+

Volume

+
+
+
+ + {/* Quick Actions */} +
+
+ + {/* Filter Bar */} + + + {/* Main Tabs */} + + + + + Express + + Buy + Sell + + + OTC + + + + + + + + + + + + + + + + + + +
- {/* Stats Cards and Balance Card */} - {user && ( -
- {/* Internal Balance Card - Takes more space */} -
- setShowDepositModal(true)} - onWithdraw={() => setShowWithdrawModal(true)} - /> -
- - {/* Stats Cards */} -
- - -
- -
-
-

{userStats.activeTrades}

-

Active Trades

-
-
-
- - -
- -
-
-

{userStats.completedTrades}

-

Completed

-
-
-
- - -
- -
-
-

${userStats.totalVolume.toLocaleString()}

-

Volume

-
-
-
-
-
- )} - -
-
-

P2P Trading

-

Buy and sell crypto with your local currency.

-
- -
- - {showCreateAd ? ( - setShowCreateAd(false)} /> - ) : ( - <> - {/* Quick Filter Bar */} - - - - - - - Express - - Buy - Sell - My Ads - - - OTC - - - -
- navigate(`/p2p/trade/${id}`)} /> -
- - -

Why Express Mode?

-
    -
  • - - Instant best-rate matching -
  • -
  • - - Verified merchants only -
  • -
  • - - Escrow protection -
  • -
  • - - No manual offer selection -
  • -
-
-
-
-
-
- - - - - - - - - - -
- -
- - -

Block Trade Benefits

-
    -
  • - - Custom pricing negotiation -
  • -
  • - - Dedicated OTC desk support -
  • -
  • - - Multi-tranche settlements -
  • -
  • - - Enhanced privacy -
  • -
  • - - Flexible payment terms -
  • -
-
-
-
-
-
-
- - )} - - {/* Deposit Modal */} + {/* Modals */} setShowDepositModal(false)} onSuccess={handleBalanceUpdated} /> - - {/* Withdraw Modal */} setShowWithdrawModal(false)} diff --git a/src/components/p2p/TradeDetail.tsx b/src/components/p2p/TradeDetail.tsx new file mode 100644 index 0000000..9d07e14 --- /dev/null +++ b/src/components/p2p/TradeDetail.tsx @@ -0,0 +1,241 @@ +import { useState, useEffect } from 'react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Badge } from '@/components/ui/badge'; +import { Clock, CheckCircle2, XCircle, AlertTriangle, Copy, Check } from 'lucide-react'; +import { getTradeById, markPaymentSent, confirmPaymentReceived, cancelTrade, type P2PFiatTrade } from '@/lib/p2p-fiat'; +import { useAuth } from '@/contexts/AuthContext'; +import { formatDistanceToNow } from 'date-fns'; +import { toast } from 'sonner'; + +interface TradeDetailProps { + tradeId: string; +} + +export function TradeDetail({ tradeId }: TradeDetailProps) { + const [trade, setTrade] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [actionLoading, setActionLoading] = useState(false); + const [copied, setCopied] = useState(false); + const { user } = useAuth(); + + const fetchTrade = async () => { + setIsLoading(true); + try { + const data = await getTradeById(tradeId); + setTrade(data); + } catch (error) { + console.error('Fetch trade error:', error); + } finally { + setIsLoading(false); + } + }; + + useEffect(() => { + fetchTrade(); + // Poll for updates + const interval = setInterval(fetchTrade, 10000); + return () => clearInterval(interval); + }, [tradeId]); + + const handleMarkPaid = async () => { + setActionLoading(true); + try { + await markPaymentSent(tradeId); + await fetchTrade(); + } finally { + setActionLoading(false); + } + }; + + const handleConfirm = async () => { + setActionLoading(true); + try { + await confirmPaymentReceived(tradeId); + await fetchTrade(); + } finally { + setActionLoading(false); + } + }; + + const handleCancel = async () => { + if (!confirm('Are you sure you want to cancel this trade?')) return; + setActionLoading(true); + try { + await cancelTrade(tradeId); + await fetchTrade(); + } finally { + setActionLoading(false); + } + }; + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + setCopied(true); + toast.success('Copied to clipboard'); + setTimeout(() => setCopied(false), 2000); + }; + + if (isLoading) { + return ( +
+
+
+ ); + } + + if (!trade) { + return ( +
+ Trade not found +
+ ); + } + + const isSeller = user?.id === trade.seller_id; + const isBuyer = user?.id === trade.buyer_id; + + const getStatusBadge = () => { + switch (trade.status) { + case 'pending': + return Waiting for Payment; + case 'payment_sent': + return Payment Sent; + case 'completed': + return Completed; + case 'cancelled': + return Cancelled; + case 'disputed': + return Disputed; + default: + return {trade.status}; + } + }; + + return ( +
+ {/* Status Card */} + + +
+ Trade Status + {getStatusBadge()} +
+
+ +
+
+ Amount + {trade.crypto_amount.toFixed(4)} HEZ +
+
+ Price + ${trade.fiat_amount.toFixed(2)} +
+
+ Rate + ${trade.price_per_unit.toFixed(4)}/HEZ +
+
+ Created + {formatDistanceToNow(new Date(trade.created_at), { addSuffix: true })} +
+ {trade.payment_deadline && trade.status === 'pending' && ( +
+ Payment Deadline + {formatDistanceToNow(new Date(trade.payment_deadline), { addSuffix: true })} +
+ )} +
+
+
+ + {/* Role-specific info */} + {isBuyer && trade.status === 'pending' && ( + + + Payment Instructions + + +

+ Send the payment to the seller and mark as paid when done. +

+
+ + +
+
+
+ )} + + {isSeller && trade.status === 'payment_sent' && ( + + + Confirm Payment + + +

+ Buyer has marked payment as sent. Check your account and confirm when received. +

+ {trade.buyer_payment_proof_url && ( + + )} + +
+
+ )} + + {trade.status === 'completed' && ( + + + +

Trade Completed!

+

+ {isBuyer ? 'HEZ has been credited to your balance.' : 'Payment has been received.'} +

+
+
+ )} + + {/* Trade ID */} + + +
+ Trade ID + +
+
+
+
+ ); +} diff --git a/src/components/p2p/TradeModal.tsx b/src/components/p2p/TradeModal.tsx index 9ea5079..386c53d 100644 --- a/src/components/p2p/TradeModal.tsx +++ b/src/components/p2p/TradeModal.tsx @@ -1,5 +1,4 @@ -import React, { useState } from 'react'; -import { useNavigate } from 'react-router-dom'; +import { useState } from 'react'; import { Dialog, DialogContent, @@ -14,37 +13,33 @@ import { Label } from '@/components/ui/label'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { Loader2, AlertTriangle, Clock } from 'lucide-react'; import { useAuth } from '@/contexts/AuthContext'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { toast } from 'sonner'; -import { acceptFiatOffer, type P2PFiatOffer } from '@shared/lib/p2p-fiat'; +import { acceptFiatOffer, type P2PFiatOffer } from '@/lib/p2p-fiat'; interface TradeModalProps { offer: P2PFiatOffer; onClose: () => void; + onTradeStarted?: (tradeId: string) => void; } -export function TradeModal({ offer, onClose }: TradeModalProps) { - const navigate = useNavigate(); +export function TradeModal({ offer, onClose, onTradeStarted }: TradeModalProps) { const { user } = useAuth(); - const { api, selectedAccount } = usePezkuwi(); const [amount, setAmount] = useState(''); const [loading, setLoading] = useState(false); const cryptoAmount = parseFloat(amount) || 0; const fiatAmount = cryptoAmount * offer.price_per_unit; const isValidAmount = cryptoAmount > 0 && cryptoAmount <= offer.remaining_amount; - - // Check min/max order amounts + const meetsMinOrder = !offer.min_order_amount || cryptoAmount >= offer.min_order_amount; const meetsMaxOrder = !offer.max_order_amount || cryptoAmount <= offer.max_order_amount; const handleInitiateTrade = async () => { - if (!api || !selectedAccount || !user) { - toast.error('Please connect your wallet and log in'); + if (!user) { + toast.error('Please log in first'); return; } - // Prevent self-trading if (offer.seller_id === user.id) { toast.error('You cannot trade with your own offer'); return; @@ -69,20 +64,16 @@ export function TradeModal({ offer, onClose }: TradeModalProps) { try { const tradeId = await acceptFiatOffer({ - api, - account: selectedAccount, offerId: offer.id, + buyerWallet: user.wallet_address || '', amount: cryptoAmount }); toast.success('Trade initiated! Proceed to payment.'); onClose(); - - // Navigate to trade page - navigate(`/p2p/trade/${tradeId}`); + onTradeStarted?.(tradeId); } catch (error) { if (import.meta.env.DEV) console.error('Accept offer error:', error); - // Error toast already shown in acceptFiatOffer } finally { setLoading(false); } @@ -90,65 +81,57 @@ export function TradeModal({ offer, onClose }: TradeModalProps) { return ( - + Buy {offer.token} - - Trading with {offer.seller_wallet.slice(0, 6)}...{offer.seller_wallet.slice(-4)} + + Rate: {offer.price_per_unit.toFixed(4)} {offer.fiat_currency}/{offer.token}
- {/* Price Info */} -
-
- Price - - {offer.price_per_unit.toFixed(2)} {offer.fiat_currency} - -
-
- Available - {offer.remaining_amount} {offer.token} -
-
- - {/* Amount Input */} -
- +
+ setAmount(e.target.value)} - placeholder="Amount" - className="bg-gray-800 border-gray-700 text-white placeholder:text-gray-500 placeholder:opacity-50" + max={offer.remaining_amount} + min={offer.min_order_amount || 0} + step="0.0001" /> - {offer.min_order_amount && ( -

- Min: {offer.min_order_amount} {offer.token} -

- )} - {offer.max_order_amount && ( -

- Max: {offer.max_order_amount} {offer.token} -

- )} +

+ Available: {offer.remaining_amount.toFixed(4)} {offer.token} +

- {/* Calculation */} {cryptoAmount > 0 && ( -
-

You will pay

-

- {fiatAmount.toFixed(2)} {offer.fiat_currency} -

+
+
+ You pay + + {fiatAmount.toFixed(2)} {offer.fiat_currency} + +
+
+ You receive + + {cryptoAmount.toFixed(4)} {offer.token} + +
)} - {/* Warnings */} - {!meetsMinOrder && cryptoAmount > 0 && ( + + + + Payment must be completed within {offer.time_limit_minutes} minutes + + + + {!meetsMinOrder && offer.min_order_amount && ( @@ -157,7 +140,7 @@ export function TradeModal({ offer, onClose }: TradeModalProps) { )} - {!meetsMaxOrder && cryptoAmount > 0 && ( + {!meetsMaxOrder && offer.max_order_amount && ( @@ -165,37 +148,18 @@ export function TradeModal({ offer, onClose }: TradeModalProps) { )} - - {/* Payment Time Limit */} - - - - Payment deadline: {offer.time_limit_minutes} minutes after accepting - -
- - diff --git a/src/components/p2p/WithdrawModal.tsx b/src/components/p2p/WithdrawModal.tsx index 39d4dd7..ea6df0d 100644 --- a/src/components/p2p/WithdrawModal.tsx +++ b/src/components/p2p/WithdrawModal.tsx @@ -1,3 +1,9 @@ +/** + * Withdraw Modal - Mobile P2P + * + * Request withdrawal from internal P2P balance to external wallet. + * Backend processes the actual blockchain transaction. + */ import { useState, useEffect } from 'react'; import { Dialog, @@ -18,26 +24,20 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { Skeleton } from '@/components/ui/skeleton'; -import { Badge } from '@/components/ui/badge'; import { Loader2, CheckCircle2, AlertTriangle, - ArrowUpFromLine, - Clock, - Info + ArrowUpFromLine } from 'lucide-react'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; import { toast } from 'sonner'; +import { useAuth } from '@/contexts/AuthContext'; import { - getInternalBalances, requestWithdraw, - getDepositWithdrawHistory, + getInternalBalance, type CryptoToken, - type InternalBalance, - type DepositWithdrawRequest -} from '@shared/lib/p2p-fiat'; + type InternalBalance +} from '@/lib/p2p-fiat'; interface WithdrawModalProps { isOpen: boolean; @@ -45,60 +45,34 @@ interface WithdrawModalProps { onSuccess?: () => void; } -type WithdrawStep = 'form' | 'confirm' | 'success'; - export function WithdrawModal({ isOpen, onClose, onSuccess }: WithdrawModalProps) { - const { selectedAccount } = usePezkuwi(); - - const [step, setStep] = useState('form'); + const { user } = useAuth(); const [token, setToken] = useState('HEZ'); const [amount, setAmount] = useState(''); const [walletAddress, setWalletAddress] = useState(''); - const [balances, setBalances] = useState([]); - const [pendingRequests, setPendingRequests] = useState([]); - const [loading, setLoading] = useState(true); - const [submitting, setSubmitting] = useState(false); - const [requestId, setRequestId] = useState(''); + const [balance, setBalance] = useState(null); + const [loading, setLoading] = useState(false); + const [success, setSuccess] = useState(false); - // Network fee estimate (in HEZ) - const NETWORK_FEE = 0.01; - const MIN_WITHDRAWAL = 0.1; - - // Fetch balances and pending requests on mount useEffect(() => { - if (isOpen) { - fetchData(); - // Pre-fill wallet address from connected account - if (selectedAccount?.address) { - setWalletAddress(selectedAccount.address); + if (isOpen && user) { + fetchBalance(); + // Pre-fill wallet from user profile + if (user.wallet_address) { + setWalletAddress(user.wallet_address); } } - }, [isOpen, selectedAccount]); + }, [isOpen, user, token]); - const fetchData = async () => { - setLoading(true); - try { - const [balanceData, historyData] = await Promise.all([ - getInternalBalances(), - getDepositWithdrawHistory() - ]); - setBalances(balanceData); - // Filter for pending withdrawal requests - setPendingRequests( - historyData.filter(r => r.request_type === 'withdraw' && r.status === 'pending') - ); - } catch (error) { - console.error('Fetch data error:', error); - } finally { - setLoading(false); - } + const fetchBalance = async () => { + const bal = await getInternalBalance(token); + setBalance(bal); }; const resetModal = () => { - setStep('form'); setAmount(''); - setSubmitting(false); - setRequestId(''); + setLoading(false); + setSuccess(false); }; const handleClose = () => { @@ -106,102 +80,83 @@ export function WithdrawModal({ isOpen, onClose, onSuccess }: WithdrawModalProps onClose(); }; - const getAvailableBalance = (): number => { - const balance = balances.find(b => b.token === token); - return balance?.available_balance || 0; - }; - - const getLockedBalance = (): number => { - const balance = balances.find(b => b.token === token); - return balance?.locked_balance || 0; - }; - - const getMaxWithdrawable = (): number => { - const available = getAvailableBalance(); - // Subtract network fee for HEZ - if (token === 'HEZ') { - return Math.max(0, available - NETWORK_FEE); + const handleWithdraw = async () => { + if (!walletAddress) { + toast.error('Please enter wallet address'); + return; + } + + const withdrawAmount = parseFloat(amount); + if (isNaN(withdrawAmount) || withdrawAmount <= 0) { + toast.error('Please enter a valid amount'); + return; + } + + if (balance && withdrawAmount > balance.available_balance) { + toast.error('Insufficient balance'); + return; + } + + setLoading(true); + + try { + await requestWithdraw(token, withdrawAmount, walletAddress); + setSuccess(true); + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); + onSuccess?.(); + } catch (error) { + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('error'); + } finally { + setLoading(false); } - return available; }; const handleSetMax = () => { - const max = getMaxWithdrawable(); - setAmount(max.toFixed(4)); - }; - - const validateWithdrawal = (): string | null => { - const withdrawAmount = parseFloat(amount); - - if (isNaN(withdrawAmount) || withdrawAmount <= 0) { - return 'Please enter a valid amount'; - } - - if (withdrawAmount < MIN_WITHDRAWAL) { - return `Minimum withdrawal is ${MIN_WITHDRAWAL} ${token}`; - } - - if (withdrawAmount > getMaxWithdrawable()) { - return 'Insufficient available balance'; - } - - if (!walletAddress || walletAddress.length < 40) { - return 'Please enter a valid wallet address'; - } - - // Check for pending requests - const hasPendingForToken = pendingRequests.some(r => r.token === token); - if (hasPendingForToken) { - return `You already have a pending ${token} withdrawal request`; - } - - return null; - }; - - const handleContinue = () => { - const error = validateWithdrawal(); - if (error) { - toast.error(error); - return; - } - setStep('confirm'); - }; - - const handleSubmitWithdrawal = async () => { - const error = validateWithdrawal(); - if (error) { - toast.error(error); - return; - } - - setSubmitting(true); - - try { - const withdrawAmount = parseFloat(amount); - const id = await requestWithdraw(token, withdrawAmount, walletAddress); - setRequestId(id); - setStep('success'); - onSuccess?.(); - } catch (error) { - console.error('Submit withdrawal error:', error); - } finally { - setSubmitting(false); + if (balance) { + setAmount(balance.available_balance.toString()); } }; - const renderFormStep = () => ( -
- {loading ? ( + if (success) { + return ( + !open && handleClose()}> + +
+
+ +
+
+

Request Submitted!

+

+ {amount} {token} withdrawal is being processed. +

+

+ Usually completes within 5-10 minutes. +

+
+ +
+
+
+ ); + } + + return ( + !open && handleClose()}> + + + + + Withdraw + + + Withdraw from P2P balance to your wallet + + +
- - - -
- ) : ( - <> - {/* Token Selection */}
- +
- {/* Balance Display */} -
-
-
-

Available

-

- {getAvailableBalance().toFixed(4)} {token} -

-
-
-

Locked (Escrow)

-

- {getLockedBalance().toFixed(4)} {token} -

-
-
-
- - {/* Amount Input */}
- -
- setAmount(e.target.value)} - min="0" - step="0.0001" - /> -
- {token} -
- + Max: {balance?.available_balance.toFixed(4) || '0'} {token} +
-

- Min: {MIN_WITHDRAWAL} {token} | Max: {getMaxWithdrawable().toFixed(4)} {token} -

+ setAmount(e.target.value)} + min="0" + max={balance?.available_balance} + step="0.0001" + />
- {/* Wallet Address */}
- + setWalletAddress(e.target.value)} placeholder="5..." className="font-mono text-xs" /> -

- Only PezkuwiChain addresses are supported -

- {/* Network Fee Info */} - {token === 'HEZ' && ( - - - - Network fee: ~{NETWORK_FEE} HEZ (deducted from withdrawal amount) - - - )} + + + + Withdrawals are processed by the platform. Network fee will be deducted. + + - {/* Pending Requests Warning */} - {pendingRequests.length > 0 && ( - - - - You have {pendingRequests.length} pending withdrawal request(s). - Please wait for them to complete. - - - )} - - - - - )} -
- ); - - const renderConfirmStep = () => { - const withdrawAmount = parseFloat(amount); - const receiveAmount = token === 'HEZ' ? withdrawAmount - NETWORK_FEE : withdrawAmount; - - return ( -
- - - - Please review your withdrawal details carefully. - This action cannot be undone. - - - -
-
- Token - {token} -
-
- Withdrawal Amount - {withdrawAmount.toFixed(4)} {token} -
- {token === 'HEZ' && ( -
- Network Fee - -{NETWORK_FEE} HEZ -
- )} -
- You Will Receive - - {receiveAmount.toFixed(4)} {token} - -
- -
-

Destination Address

-

{walletAddress}

-
- -
- - Processing time: Usually within 5-30 minutes -
- - - - - -
- ); - }; - - const renderSuccessStep = () => ( -
-
- -
- -
-

- Withdrawal Request Submitted! -

-

- Your withdrawal request has been submitted for processing. -

-
- -
-
- Request ID - - {requestId.slice(0, 8)}... - -
-
- Status - - - Processing - -
-
- Amount - {amount} {token} -
-
- - - - - You can track your withdrawal status in the transaction history. - Funds will arrive in your wallet within 5-30 minutes. - - - - -
- ); - - return ( - !open && handleClose()}> - - - - - Withdraw from P2P Balance - - {step !== 'success' && ( - - {step === 'form' && 'Withdraw crypto from your P2P balance to external wallet'} - {step === 'confirm' && 'Review and confirm your withdrawal'} - - )} - - - {step === 'form' && renderFormStep()} - {step === 'confirm' && renderConfirmStep()} - {step === 'success' && renderSuccessStep()} ); diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx index 0786d71..0465633 100644 --- a/src/contexts/AuthContext.tsx +++ b/src/contexts/AuthContext.tsx @@ -1,309 +1,278 @@ -import React, { createContext, useContext, useEffect, useState, useCallback } from 'react'; +import { createContext, useContext, useState, useEffect, useCallback, type ReactNode } from 'react'; import { supabase } from '@/lib/supabase'; -import { User } from '@supabase/supabase-js'; -import { isMobileApp, getNativeWalletAddress, getNativeAccountName } from '@/lib/mobile-bridge'; -// Session timeout configuration -const SESSION_TIMEOUT_MS = 30 * 60 * 1000; // 30 minutes -const ACTIVITY_CHECK_INTERVAL_MS = 60 * 1000; // Check every 1 minute -const LAST_ACTIVITY_KEY = 'last_activity_timestamp'; -const REMEMBER_ME_KEY = 'remember_me'; +// Telegram WebApp types +declare global { + interface Window { + Telegram?: { + WebApp: { + initData: string; + initDataUnsafe: { + user?: { + id: number; + first_name: string; + last_name?: string; + username?: string; + language_code?: string; + photo_url?: string; + }; + auth_date: number; + hash: string; + }; + ready: () => void; + expand: () => void; + close: () => void; + MainButton: { + text: string; + show: () => void; + hide: () => void; + onClick: (callback: () => void) => void; + }; + HapticFeedback: { + impactOccurred: (style: 'light' | 'medium' | 'heavy' | 'rigid' | 'soft') => void; + notificationOccurred: (type: 'error' | 'success' | 'warning') => void; + selectionChanged: () => void; + }; + }; + }; + } +} + +interface TelegramUser { + id: number; + first_name: string; + last_name?: string; + username?: string; + photo_url?: string; +} + +interface User { + id: string; // Supabase user ID + telegram_id: number; + telegram_username?: string; + display_name: string; + avatar_url?: string; + wallet_address?: string; + created_at: string; +} interface AuthContextType { user: User | null; - loading: boolean; - isAdmin: boolean; - signIn: (email: string, password: string, rememberMe?: boolean) => Promise<{ error: Error | null }>; - signUp: (email: string, password: string, username: string, referralCode?: string) => Promise<{ error: Error | null }>; - signOut: () => Promise; - checkAdminStatus: () => Promise; + telegramUser: TelegramUser | null; + isLoading: boolean; + isAuthenticated: boolean; + error: string | null; + login: () => Promise; + logout: () => void; + linkWallet: (address: string) => Promise; } -const AuthContext = createContext(undefined); +const AuthContext = createContext(null); -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 }) => { +export function AuthProvider({ children }: { children: ReactNode }) { const [user, setUser] = useState(null); - const [loading, setLoading] = useState(true); - const [isAdmin, setIsAdmin] = useState(false); + const [telegramUser, setTelegramUser] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); - // ======================================== - // SESSION TIMEOUT MANAGEMENT - // ======================================== - - // Update last activity timestamp - const updateLastActivity = useCallback(() => { - - localStorage.setItem(LAST_ACTIVITY_KEY, Date.now().toString()); + // Get Telegram user from WebApp + const getTelegramUser = useCallback((): TelegramUser | null => { + const tg = window.Telegram?.WebApp; + if (!tg?.initDataUnsafe?.user) { + return null; + } + return tg.initDataUnsafe.user; }, []); - const signOut = useCallback(async () => { - setIsAdmin(false); - setUser(null); - localStorage.removeItem(LAST_ACTIVITY_KEY); - localStorage.removeItem(REMEMBER_ME_KEY); - await supabase.auth.signOut(); - }, []); - - // Check if session has timed out - const checkSessionTimeout = useCallback(async () => { - if (!user) return; - - // Skip timeout check if "Remember Me" is enabled - const rememberMe = localStorage.getItem(REMEMBER_ME_KEY); - if (rememberMe === 'true') { - return; // Don't timeout if user chose to be remembered - } - - const lastActivity = localStorage.getItem(LAST_ACTIVITY_KEY); - if (!lastActivity) { - updateLastActivity(); - return; - } - - const lastActivityTime = parseInt(lastActivity, 10); - const now = Date.now(); - const inactiveTime = now - lastActivityTime; - - if (inactiveTime >= SESSION_TIMEOUT_MS) { - if (import.meta.env.DEV) console.log('⏱️ Session timeout - logging out due to inactivity'); - await signOut(); - } - }, [user, updateLastActivity, signOut]); - - // Setup activity listeners - useEffect(() => { - if (!user) return; - - // Update activity on user interactions - const activityEvents = ['mousedown', 'keydown', 'scroll', 'touchstart']; - - const handleActivity = () => { - updateLastActivity(); - - - }; - - // Register event listeners - activityEvents.forEach((event) => { - window.addEventListener(event, handleActivity); - }); - - // Initial activity timestamp - updateLastActivity(); - - - - // Check for timeout periodically - const timeoutChecker = setInterval(checkSessionTimeout, ACTIVITY_CHECK_INTERVAL_MS); - - // Cleanup - return () => { - activityEvents.forEach((event) => { - window.removeEventListener(event, handleActivity); - }); - clearInterval(timeoutChecker); - }; - }, [user, updateLastActivity, checkSessionTimeout]); - - - const checkAdminStatus = useCallback(async () => { - // Admin wallet whitelist (blockchain-based auth) - const ADMIN_WALLETS = [ - '5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY', // Founder (original) - '5DFwqK698vL4gXHEcanaewnAqhxJ2rjhAogpSTHw3iwGDwd3', // Founder delegate (initial KYC member) - '5GgTgG9sRmPQAYU1RsTejZYnZRjwzKZKWD3awtuqjHioki45', // Founder (current dev wallet) - ]; + // Login with Telegram + const login = useCallback(async () => { + setIsLoading(true); + setError(null); try { - // PRIMARY: Check wallet-based admin (blockchain auth) - const connectedWallet = localStorage.getItem('selectedWallet'); - if (import.meta.env.DEV) console.log('🔍 Admin check - Connected wallet:', connectedWallet); - if (import.meta.env.DEV) console.log('🔍 Admin check - Whitelist:', ADMIN_WALLETS); + const tg = window.Telegram?.WebApp; - if (connectedWallet && ADMIN_WALLETS.includes(connectedWallet)) { - if (import.meta.env.DEV) console.log('✅ Admin access granted (wallet-based)'); - setIsAdmin(true); - return true; + if (!tg?.initData) { + throw new Error('Telegram WebApp not available. Open from Telegram.'); } - // SECONDARY: Check Supabase admin_roles (if wallet not in whitelist) - const { data: { user } } = await supabase.auth.getUser(); - if (user) { - const { data, error } = await supabase - .from('admin_roles') - .select('role') - .eq('user_id', user.id) - .maybeSingle(); + // Call Supabase Edge Function to verify initData and get/create user + const { data, error: fnError } = await supabase.functions.invoke('telegram-auth', { + body: { initData: tg.initData } + }); - if (!error && data && ['admin', 'super_admin'].includes(data.role)) { - if (import.meta.env.DEV) console.log('✅ Admin access granted (Supabase-based)'); - setIsAdmin(true); - return true; - } + if (fnError) throw fnError; + + if (!data?.user) { + throw new Error('Authentication failed'); } - if (import.meta.env.DEV) console.log('❌ Admin access denied'); - setIsAdmin(false); - return false; + setUser(data.user); + setTelegramUser(getTelegramUser()); + + // Store session token if provided + if (data.session_token) { + localStorage.setItem('p2p_session', data.session_token); + } + + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); } catch (err) { - if (import.meta.env.DEV) console.error('Admin check error:', err); - setIsAdmin(false); + const message = err instanceof Error ? err.message : 'Login failed'; + setError(message); + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('error'); + console.error('Login error:', err); + } finally { + setIsLoading(false); + } + }, [getTelegramUser]); + + // Logout + const logout = useCallback(() => { + setUser(null); + localStorage.removeItem('p2p_session'); + window.Telegram?.WebApp.HapticFeedback.impactOccurred('medium'); + }, []); + + // Link wallet address + const linkWallet = useCallback(async (address: string) => { + if (!user) throw new Error('Not authenticated'); + + const { error: updateError } = await supabase + .from('p2p_users') + .update({ wallet_address: address }) + .eq('telegram_id', user.telegram_id); + + if (updateError) throw updateError; + + setUser(prev => prev ? { ...prev, wallet_address: address } : null); + window.Telegram?.WebApp.HapticFeedback.notificationOccurred('success'); + }, [user]); + + // Login via URL params (from mini-app redirect) + const loginViaParams = useCallback(async () => { + const params = new URLSearchParams(window.location.search); + const tgId = params.get('tg_id'); + const wallet = params.get('wallet'); + const from = params.get('from'); + const ts = params.get('ts'); + + if (!tgId || from !== 'miniapp') { return false; } - }, []); - // Setup native mobile wallet if running in mobile app - const setupMobileWallet = useCallback(() => { - if (isMobileApp()) { - const nativeAddress = getNativeWalletAddress(); - const nativeAccountName = getNativeAccountName(); - - if (nativeAddress) { - // Store native wallet address for admin checks and wallet operations - localStorage.setItem('selectedWallet', nativeAddress); - if (nativeAccountName) { - localStorage.setItem('selectedWalletName', nativeAccountName); - } - if (import.meta.env.DEV) { - console.log('[Mobile] Native wallet detected:', nativeAddress); - } - // Dispatch wallet change event - window.dispatchEvent(new Event('walletChanged')); + // Validate timestamp (not older than 5 minutes) + if (ts) { + const timestamp = parseInt(ts); + const now = Date.now(); + if (now - timestamp > 5 * 60 * 1000) { + console.warn('URL params expired'); + return false; } } + + setIsLoading(true); + + try { + // Verify with backend and get/create user + const { data, error: fnError } = await supabase.functions.invoke('telegram-auth', { + body: { + telegram_id: parseInt(tgId), + wallet_address: wallet || undefined, + from_miniapp: true + } + }); + + if (fnError) throw fnError; + + if (!data?.user) { + throw new Error('Authentication failed'); + } + + setUser(data.user); + + // Store session token + if (data.session_token) { + localStorage.setItem('p2p_session', data.session_token); + } + + // Clear URL params after successful login + window.history.replaceState({}, '', window.location.pathname); + + return true; + } catch (err) { + console.error('URL param login error:', err); + return false; + } finally { + setIsLoading(false); + } }, []); + // Auto-login on mount useEffect(() => { - // Setup mobile wallet first - setupMobileWallet(); + const initAuth = async () => { + const tg = window.Telegram?.WebApp; - // Check active sessions and sets the user - supabase.auth.getSession().then(({ data: { session } }) => { - setUser(session?.user ?? null); - checkAdminStatus(); // Check admin status regardless of Supabase session - setLoading(false); - }).catch(() => { - // If Supabase is not available, still check wallet-based admin - checkAdminStatus(); - setLoading(false); - }); - - // Listen for changes on auth state - const { data: { subscription } } = supabase.auth.onAuthStateChange((_event, session) => { - setUser(session?.user ?? null); - checkAdminStatus(); // Check admin status on auth change - setLoading(false); - }); - - // Listen for wallet changes (from PezkuwiContext or native bridge) - const handleWalletChange = () => { - checkAdminStatus(); - }; - window.addEventListener('walletChanged', handleWalletChange); - - // Listen for native bridge ready event (mobile app) - const handleNativeReady = () => { - if (import.meta.env.DEV) { - console.log('[Mobile] Native bridge ready'); - } - setupMobileWallet(); - checkAdminStatus(); - }; - window.addEventListener('pezkuwi-native-ready', handleNativeReady); - - return () => { - subscription.unsubscribe(); - window.removeEventListener('walletChanged', handleWalletChange); - window.removeEventListener('pezkuwi-native-ready', handleNativeReady); - }; - }, [checkAdminStatus, setupMobileWallet]); - - const signIn = async (email: string, password: string, rememberMe: boolean = false) => { - try { - const { data, error } = await supabase.auth.signInWithPassword({ - email, - password, - }); - - if (!error && data.user) { - // Store remember me preference - if (rememberMe) { - localStorage.setItem(REMEMBER_ME_KEY, 'true'); - } else { - localStorage.removeItem(REMEMBER_ME_KEY); - } - await checkAdminStatus(); - } - - return { error }; - } catch { - return { - error: { - message: 'Authentication service unavailable. Please try again later.' - } - }; - } - }; - - const signUp = async (email: string, password: string, username: string, referralCode?: string) => { - try { - const { data, error } = await supabase.auth.signUp({ - email, - password, - options: { - data: { - username, - referral_code: referralCode || null, - }, - }, - }); - - if (!error && data.user) { - // Create profile in profiles table with referral code - await supabase.from('profiles').insert({ - id: data.user.id, - username, - email, - referred_by: referralCode || null, - }); - - // If there's a referral code, track it - if (referralCode) { - // You can add logic here to reward the referrer - // For example, update their referral count or add rewards - if (import.meta.env.DEV) console.log(`User registered with referral code: ${referralCode}`); + // Check for existing session first + const sessionToken = localStorage.getItem('p2p_session'); + if (sessionToken) { + try { + const { data, error } = await supabase.functions.invoke('telegram-auth', { + body: { sessionToken } + }); + if (!error && data?.user) { + setUser(data.user); + setIsLoading(false); + return; + } + } catch { + localStorage.removeItem('p2p_session'); } } - return { error }; - } catch { - return { - error: { - message: 'Registration service unavailable. Please try again later.' - } - }; - } + // Try Telegram WebApp auth + if (tg?.initData) { + tg.ready(); + tg.expand(); + setTelegramUser(getTelegramUser()); + await login(); + return; + } + + // Try URL params auth (from mini-app redirect) + const params = new URLSearchParams(window.location.search); + if (params.get('from') === 'miniapp' && params.get('tg_id')) { + const success = await loginViaParams(); + if (success) return; + } + + setIsLoading(false); + }; + + initAuth(); + }, [getTelegramUser, login, loginViaParams]); + + const value: AuthContextType = { + user, + telegramUser, + isLoading, + isAuthenticated: !!user, + error, + login, + logout, + linkWallet }; return ( - + {children} ); -}; \ No newline at end of file +} + +export function useAuth() { + const context = useContext(AuthContext); + if (!context) { + throw new Error('useAuth must be used within AuthProvider'); + } + return context; +} diff --git a/src/contexts/DashboardContext.tsx b/src/contexts/DashboardContext.tsx deleted file mode 100644 index 8ae46b2..0000000 --- a/src/contexts/DashboardContext.tsx +++ /dev/null @@ -1,98 +0,0 @@ -import { createContext, useContext, useState, useEffect, useCallback, ReactNode } from 'react'; -import { useAuth } from '@/contexts/AuthContext'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; -import { supabase } from '@/lib/supabase'; -import { getAllTikiNFTDetails, generateCitizenNumber, type TikiNFTDetails } from '@pezkuwi/lib/tiki'; -import { getKycStatus } from '@pezkuwi/lib/kyc'; - -interface DashboardData { - profile: Record | null | null; - nftDetails: { citizenNFT: TikiNFTDetails | null; roleNFTs: TikiNFTDetails[]; totalNFTs: number }; - kycStatus: string; - citizenNumber: string; - loading: boolean; -} - -const DashboardContext = createContext(undefined); - -export function DashboardProvider({ children }: { children: ReactNode }) { - const { user } = useAuth(); - const { api, isApiReady, selectedAccount } = usePezkuwi(); - const [profile, setProfile] = useState | null>(null); - const [nftDetails, setNftDetails] = useState<{ citizenNFT: TikiNFTDetails | null; roleNFTs: TikiNFTDetails[]; totalNFTs: number }>({ - citizenNFT: null, - roleNFTs: [], - totalNFTs: 0 - }); - const [kycStatus, setKycStatus] = useState('NotStarted'); - const [loading, setLoading] = useState(true); - - const fetchProfile = useCallback(async () => { - if (!user) { - setLoading(false); - return; - } - - try { - const { data, error } = await supabase - .from('profiles') - .select('*') - .eq('id', user.id) - .maybeSingle(); - - if (error) { - if (import.meta.env.DEV) console.warn('Profile fetch error (this is normal if Supabase is not configured):', error.message); - return; - } - - setProfile(data); - } catch (error) { - if (import.meta.env.DEV) console.warn('Error fetching profile (this is normal if Supabase is not configured):', error); - } finally { - setLoading(false); - } - }, [user]); - - const fetchScoresAndTikis = useCallback(async () => { - if (!selectedAccount || !api) return; - - setLoading(true); - try { - const status = await getKycStatus(api, selectedAccount.address); - setKycStatus(status); - - const details = await getAllTikiNFTDetails(api, selectedAccount.address); - setNftDetails(details); - } catch (error) { - if (import.meta.env.DEV) console.error('Error fetching data:', error); - } finally { - setLoading(false); - } - }, [selectedAccount, api]); - - useEffect(() => { - fetchProfile(); - if (selectedAccount && api && isApiReady) { - fetchScoresAndTikis(); - - } - }, [user, selectedAccount, api, isApiReady, fetchProfile, fetchScoresAndTikis]); - - const citizenNumber = nftDetails.citizenNFT - ? generateCitizenNumber(nftDetails.citizenNFT.owner, nftDetails.citizenNFT.collectionId, nftDetails.citizenNFT.itemId) - : 'N/A'; - - return ( - - {children} - - ); -} - -export function useDashboard() { - const context = useContext(DashboardContext); - if (context === undefined) { - throw new Error('useDashboard must be used within a DashboardProvider'); - } - return context; -} diff --git a/src/contexts/PezkuwiContext.tsx b/src/contexts/PezkuwiContext.tsx deleted file mode 100644 index add1077..0000000 --- a/src/contexts/PezkuwiContext.tsx +++ /dev/null @@ -1,330 +0,0 @@ -import React, { createContext, useContext, useEffect, useState, ReactNode } from 'react'; -import { ApiPromise, WsProvider } from '@pezkuwi/api'; -import { web3Accounts, web3Enable } from '@pezkuwi/extension-dapp'; -import type { InjectedAccountWithMeta } from '@pezkuwi/extension-inject/types'; -import { DEFAULT_ENDPOINT } from '../../../shared/blockchain/pezkuwi'; -import { isMobileApp, getNativeWalletAddress, getNativeAccountName } from '@/lib/mobile-bridge'; - -interface PezkuwiContextType { - api: ApiPromise | null; - isApiReady: boolean; - isConnected: boolean; - accounts: InjectedAccountWithMeta[]; - selectedAccount: InjectedAccountWithMeta | null; - setSelectedAccount: (account: InjectedAccountWithMeta | null) => void; - connectWallet: () => Promise; - disconnectWallet: () => void; - error: string | null; - sudoKey: string | null; -} - -const PezkuwiContext = createContext(undefined); - -interface PezkuwiProviderProps { - children: ReactNode; - endpoint?: string; -} - -export const PezkuwiProvider: React.FC = ({ - children, - endpoint = DEFAULT_ENDPOINT // Beta testnet RPC from shared config -}) => { - const [api, setApi] = useState(null); - const [isApiReady, setIsApiReady] = useState(false); - const [accounts, setAccounts] = useState([]); - const [selectedAccount, setSelectedAccount] = useState(null); - const [error, setError] = useState(null); - const [sudoKey, setSudoKey] = useState(null); - - // Wrapper to trigger events when wallet changes - const handleSetSelectedAccount = (account: InjectedAccountWithMeta | null) => { - setSelectedAccount(account); - if (account) { - localStorage.setItem('selectedWallet', account.address); - if (import.meta.env.DEV) { - if (import.meta.env.DEV) console.log('💾 Wallet saved:', account.address); - } - window.dispatchEvent(new Event('walletChanged')); - } else { - localStorage.removeItem('selectedWallet'); - window.dispatchEvent(new Event('walletChanged')); - } - }; - - // Initialize Pezkuwi API with fallback endpoints - useEffect(() => { - const FALLBACK_ENDPOINTS = [ - endpoint, - import.meta.env.VITE_WS_ENDPOINT_FALLBACK_1, - import.meta.env.VITE_WS_ENDPOINT_FALLBACK_2, - ].filter(Boolean); - - const initApi = async () => { - let lastError: unknown = null; - - for (const currentEndpoint of FALLBACK_ENDPOINTS) { - try { - if (import.meta.env.DEV) { - if (import.meta.env.DEV) console.log('🔗 Connecting to Pezkuwi node:', currentEndpoint); - } - - const provider = new WsProvider(currentEndpoint); - const apiInstance = await ApiPromise.create({ provider }); - - await apiInstance.isReady; - - setApi(apiInstance); - setIsApiReady(true); - setError(null); - - if (import.meta.env.DEV) { - if (import.meta.env.DEV) console.log('✅ Connected to Pezkuwi node'); - - // Get chain info - const [chain, nodeName, nodeVersion] = await Promise.all([ - apiInstance.rpc.system.chain(), - apiInstance.rpc.system.name(), - apiInstance.rpc.system.version(), - ]); - - if (import.meta.env.DEV) console.log(`📡 Chain: ${chain}`); - if (import.meta.env.DEV) console.log(`🖥️ Node: ${nodeName} v${nodeVersion}`); - } - - // Fetch sudo key from blockchain - try { - const sudoAccount = await apiInstance.query.sudo.key(); - const sudoAddress = sudoAccount.toString(); - setSudoKey(sudoAddress); - if (import.meta.env.DEV) console.log(`🔑 Sudo key: ${sudoAddress}`); - } catch (err) { - if (import.meta.env.DEV) console.warn('⚠️ Failed to fetch sudo key (sudo pallet may not be available):', err); - } - - return; - } catch (err) { - lastError = err; - if (import.meta.env.DEV) { - if (import.meta.env.DEV) console.warn(`⚠️ Failed to connect to ${currentEndpoint}, trying next...`); - } - continue; - } - } - - if (import.meta.env.DEV) { - if (import.meta.env.DEV) console.error('❌ Failed to connect to all endpoints:', lastError); - } - setError('Failed to connect to blockchain network. Please try again later.'); - setIsApiReady(false); - }; - - initApi(); - - return () => { - if (api) { - api.disconnect(); - } - }; - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [endpoint]); - - // Auto-restore wallet on page load (or setup mobile wallet) - useEffect(() => { - const restoreWallet = async () => { - // Check if running in mobile app - if (isMobileApp()) { - const nativeAddress = getNativeWalletAddress(); - const nativeAccountName = getNativeAccountName(); - - if (nativeAddress) { - // Create a virtual account for the mobile wallet - const mobileAccount: InjectedAccountWithMeta = { - address: nativeAddress, - meta: { - name: nativeAccountName || 'Mobile Wallet', - source: 'pezkuwi-mobile', - }, - type: 'sr25519', - }; - - setAccounts([mobileAccount]); - handleSetSelectedAccount(mobileAccount); - - if (import.meta.env.DEV) { - console.log('[Mobile] Native wallet connected:', nativeAddress.slice(0, 8) + '...'); - } - return; - } - } - - // Desktop: Try to restore from localStorage - const savedAddress = localStorage.getItem('selectedWallet'); - if (!savedAddress) return; - - try { - // Enable extension - const extensions = await web3Enable('PezkuwiChain'); - if (extensions.length === 0) return; - - // Get accounts - const allAccounts = await web3Accounts(); - if (allAccounts.length === 0) return; - - // Find saved account - const savedAccount = allAccounts.find(acc => acc.address === savedAddress); - if (savedAccount) { - setAccounts(allAccounts); - handleSetSelectedAccount(savedAccount); - if (import.meta.env.DEV) { - console.log('✅ Wallet restored:', savedAddress.slice(0, 8) + '...'); - } - } - } catch (err) { - if (import.meta.env.DEV) { - console.error('Failed to restore wallet:', err); - } - } - }; - - restoreWallet(); - - // Listen for native bridge ready event (mobile) - const handleNativeReady = () => { - if (import.meta.env.DEV) { - console.log('[Mobile] Native bridge ready, restoring wallet'); - } - restoreWallet(); - }; - - window.addEventListener('pezkuwi-native-ready', handleNativeReady); - - return () => { - window.removeEventListener('pezkuwi-native-ready', handleNativeReady); - }; - }, []); - - // Connect wallet (Pezkuwi.js extension or native mobile) - const connectWallet = async () => { - try { - setError(null); - - // Check if running in mobile app - if (isMobileApp()) { - const nativeAddress = getNativeWalletAddress(); - const nativeAccountName = getNativeAccountName(); - - if (nativeAddress) { - // Create a virtual account for the mobile wallet - const mobileAccount: InjectedAccountWithMeta = { - address: nativeAddress, - meta: { - name: nativeAccountName || 'Mobile Wallet', - source: 'pezkuwi-mobile', - }, - type: 'sr25519', - }; - - setAccounts([mobileAccount]); - handleSetSelectedAccount(mobileAccount); - - if (import.meta.env.DEV) { - console.log('[Mobile] Native wallet connected:', nativeAddress.slice(0, 8) + '...'); - } - return; - } else { - // Request wallet connection from native app - setError('Please connect your wallet in the app'); - return; - } - } - - // Desktop: Check if extension is installed first - const hasExtension = !!(window as unknown as { injectedWeb3?: Record }).injectedWeb3; - - // Enable extension - const extensions = await web3Enable('PezkuwiChain'); - - if (extensions.length === 0) { - if (hasExtension) { - // Extension is installed but user didn't authorize - don't redirect - setError('Please authorize the connection in your Pezkuwi Wallet extension'); - } else { - // Extension not installed - show install link - setError('Pezkuwi Wallet extension not found. Please install from Chrome Web Store.'); - window.open('https://chrome.google.com/webstore/detail/pezkuwi-wallet/fbnboicjjeebjhgnapneaeccpgjcdibn', '_blank'); - } - return; - } - - if (import.meta.env.DEV) { - console.log('✅ Pezkuwi.js extension enabled'); - } - - // Get accounts - const allAccounts = await web3Accounts(); - - if (allAccounts.length === 0) { - setError('No accounts found. Please create an account in Pezkuwi.js extension'); - return; - } - - setAccounts(allAccounts); - - // Try to restore previously selected account, otherwise use first - const savedAddress = localStorage.getItem('selectedWallet'); - const accountToSelect = savedAddress - ? allAccounts.find(acc => acc.address === savedAddress) || allAccounts[0] - : allAccounts[0]; - - // Use wrapper to trigger events - handleSetSelectedAccount(accountToSelect); - - if (import.meta.env.DEV) { - console.log(`✅ Found ${allAccounts.length} account(s)`); - } - - } catch (err) { - if (import.meta.env.DEV) { - console.error('❌ Wallet connection failed:', err); - } - setError('Failed to connect wallet'); - } - }; - - // Disconnect wallet - const disconnectWallet = () => { - setAccounts([]); - handleSetSelectedAccount(null); - if (import.meta.env.DEV) { - if (import.meta.env.DEV) console.log('🔌 Wallet disconnected'); - } - }; - - const value: PezkuwiContextType = { - api, - isApiReady, - isConnected: isApiReady, // Alias for backward compatibility - accounts, - selectedAccount, - setSelectedAccount: handleSetSelectedAccount, - connectWallet, - disconnectWallet, - error, - sudoKey, - }; - - return ( - - {children} - - ); -}; - -// Hook to use Pezkuwi context -export const usePezkuwi = (): PezkuwiContextType => { - const context = useContext(PezkuwiContext); - if (!context) { - throw new Error('usePezkuwi must be used within PezkuwiProvider'); - } - return context; -}; diff --git a/src/contexts/ReferralContext.tsx b/src/contexts/ReferralContext.tsx deleted file mode 100644 index 17f5f84..0000000 --- a/src/contexts/ReferralContext.tsx +++ /dev/null @@ -1,177 +0,0 @@ -import { createContext, useContext, useState, useEffect, ReactNode, useCallback } from 'react'; -import { usePezkuwi } from '@/contexts/PezkuwiContext'; -import { useWallet } from '@/contexts/WalletContext'; -import { useToast } from '@/hooks/use-toast'; -import { - getReferralStats, - getMyReferrals, - initiateReferral, - subscribeToReferralEvents, - type ReferralStats, -} from '@pezkuwi/lib/referral'; - -interface ReferralContextValue { - stats: ReferralStats | null; - myReferrals: string[]; - loading: boolean; - inviteUser: (referredAddress: string) => Promise; - refreshStats: () => Promise; -} - -const ReferralContext = createContext(undefined); - -export function ReferralProvider({ children }: { children: ReactNode }) { - const { api, isApiReady } = usePezkuwi(); - const { account } = useWallet(); - const { toast } = useToast(); - - const [stats, setStats] = useState(null); - const [myReferrals, setMyReferrals] = useState([]); - const [loading, setLoading] = useState(true); - - // Fetch referral statistics - const fetchStats = useCallback(async () => { - if (!api || !isApiReady || !account) { - setStats(null); - setMyReferrals([]); - setLoading(false); - return; - } - - try { - setLoading(true); - - const [fetchedStats, fetchedReferrals] = await Promise.all([ - getReferralStats(api, account), - getMyReferrals(api, account), - ]); - - setStats(fetchedStats); - setMyReferrals(fetchedReferrals); - } catch (error) { - if (import.meta.env.DEV) console.error('Error fetching referral stats:', error); - toast({ - title: 'Error', - description: 'Failed to load referral statistics', - variant: 'destructive', - }); - } finally { - setLoading(false); - } - }, [api, isApiReady, account, toast]); - - // Initial fetch - useEffect(() => { - fetchStats(); - }, [fetchStats]); - - // Subscribe to referral events for real-time updates - useEffect(() => { - if (!api || !isApiReady || !account) return; - - let unsub: (() => void) | undefined; - - subscribeToReferralEvents(api, (event) => { - // If this user is involved in the event, refresh stats - if (event.referrer === account || event.referred === account) { - if (event.type === 'initiated') { - toast({ - title: 'Referral Sent', - description: `Invitation sent to ${event.referred.slice(0, 8)}...`, - }); - } else if (event.type === 'confirmed') { - toast({ - title: 'Referral Confirmed!', - description: `Your referral completed KYC. Total: ${event.count}`, - variant: 'default', - }); - } - fetchStats(); - } - }).then((unsubFn) => { - unsub = unsubFn; - }); - - return () => { - if (unsub) unsub(); - }; - }, [api, isApiReady, account, toast, fetchStats]); - - // Invite a new user - const inviteUser = async (referredAddress: string): Promise => { - if (!api || !account) { - toast({ - title: 'Error', - description: 'Wallet not connected', - variant: 'destructive', - }); - return false; - } - - try { - // Validate address format - if (!referredAddress || referredAddress.length < 47) { - toast({ - title: 'Invalid Address', - description: 'Please enter a valid Pezkuwi address', - variant: 'destructive', - }); - return false; - } - - toast({ - title: 'Sending Invitation', - description: 'Please sign the transaction...', - }); - - await initiateReferral(api, { address: account, meta: { source: 'pezkuwi' } } as Record, referredAddress); - - toast({ - title: 'Success!', - description: 'Referral invitation sent successfully', - }); - - // Refresh stats after successful invitation - await fetchStats(); - return true; - } catch (error) { - if (import.meta.env.DEV) console.error('Error inviting user:', error); - - let errorMessage = 'Failed to send referral invitation'; - if (error.message) { - if (error.message.includes('SelfReferral')) { - errorMessage = 'You cannot refer yourself'; - } else if (error.message.includes('AlreadyReferred')) { - errorMessage = 'This user has already been referred'; - } else { - errorMessage = error.message; - } - } - - toast({ - title: 'Error', - description: errorMessage, - variant: 'destructive', - }); - return false; - } - }; - - const value: ReferralContextValue = { - stats, - myReferrals, - loading, - inviteUser, - refreshStats: fetchStats, - }; - - return {children}; -} - -export function useReferral() { - const context = useContext(ReferralContext); - if (context === undefined) { - throw new Error('useReferral must be used within a ReferralProvider'); - } - return context; -} diff --git a/src/contexts/WalletContext.tsx b/src/contexts/WalletContext.tsx index 76f117a..1312a5d 100644 --- a/src/contexts/WalletContext.tsx +++ b/src/contexts/WalletContext.tsx @@ -1,314 +1,25 @@ -// ======================================== -// WalletContext - Pezkuwi.js Wallet Integration -// ======================================== -// This context wraps PezkuwiContext and provides wallet functionality -// ⚠️ MIGRATION NOTE: This now uses Pezkuwi.js instead of MetaMask/Ethereum - -import React, { createContext, useContext, useState, useEffect, useCallback } from 'react'; -import { usePezkuwi } from './PezkuwiContext'; -import { WALLET_ERRORS, formatBalance, ASSET_IDS } from '@pezkuwi/lib/wallet'; -import type { InjectedAccountWithMeta } from '@pezkuwi/extension-inject/types'; -import type { Signer } from '@pezkuwi/api/types'; -import { web3FromAddress } from '@pezkuwi/extension-dapp'; -import { isMobileApp, signTransactionNative, type TransactionPayload } from '@/lib/mobile-bridge'; - -interface TokenBalances { - HEZ: string; - PEZ: string; - wHEZ: string; - USDT: string; // User-facing key for wUSDT (backend uses wUSDT asset ID 2) -} +/** + * Wallet Context for P2P Mobile + * + * In the mobile P2P app, wallet address is linked to user's profile. + * No direct blockchain API connection needed - trades use internal ledger. + */ +import { createContext, useContext, type ReactNode } from 'react'; +import { useAuth } from './AuthContext'; interface WalletContextType { + address: string | null; isConnected: boolean; - account: string | null; // Current selected account address - accounts: InjectedAccountWithMeta[]; - balance: string; // Legacy: HEZ balance - balances: TokenBalances; // All token balances - error: string | null; - signer: Signer | null; // Pezkuwi.js signer for transactions - connectWallet: () => Promise; - disconnect: () => void; - switchAccount: (account: InjectedAccountWithMeta) => void; - signTransaction: (tx: unknown) => Promise; - signMessage: (message: string) => Promise; - refreshBalances: () => Promise; // Refresh all token balances } -const WalletContext = createContext(undefined); +const WalletContext = createContext(null); -export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { - const pezkuwi = usePezkuwi(); - - if (import.meta.env.DEV) console.log('🎯 WalletProvider render:', { - hasApi: !!pezkuwi.api, - isApiReady: pezkuwi.isApiReady, - selectedAccount: pezkuwi.selectedAccount?.address, - accountsCount: pezkuwi.accounts.length - }); - - const [balance, setBalance] = useState('0'); - const [balances, setBalances] = useState({ HEZ: '0', PEZ: '0', wHEZ: '0', USDT: '0' }); - const [error, setError] = useState(null); - const [signer, setSigner] = useState(null); - - // Fetch all token balances when account changes - const updateBalance = useCallback(async (address: string) => { - if (!pezkuwi.api || !pezkuwi.isApiReady) { - if (import.meta.env.DEV) console.warn('API not ready, cannot fetch balance'); - return; - } - - try { - if (import.meta.env.DEV) console.log('💰 Fetching all token balances for:', address); - - // Fetch HEZ (native token) - const { data: nativeBalance } = await pezkuwi.api.query.system.account(address); - const hezBalance = formatBalance(nativeBalance.free.toString()); - setBalance(hezBalance); // Legacy support - - // Fetch PEZ (Asset ID: 1) - let pezBalance = '0'; - try { - const pezData = await pezkuwi.api.query.assets.account(ASSET_IDS.PEZ, address); - if (import.meta.env.DEV) console.log('📊 Raw PEZ data:', pezData.toHuman()); - - if (pezData.isSome) { - const assetData = pezData.unwrap(); - const pezAmount = assetData.balance.toString(); - pezBalance = formatBalance(pezAmount); - if (import.meta.env.DEV) console.log('✅ PEZ balance found:', pezBalance); - } else { - if (import.meta.env.DEV) console.warn('⚠️ PEZ asset not found for this account'); - } - } catch (err) { - if (import.meta.env.DEV) console.error('❌ Failed to fetch PEZ balance:', err); - } - - // Fetch wHEZ (Asset ID: 0) - let whezBalance = '0'; - try { - const whezData = await pezkuwi.api.query.assets.account(ASSET_IDS.WHEZ, address); - if (import.meta.env.DEV) console.log('📊 Raw wHEZ data:', whezData.toHuman()); - - if (whezData.isSome) { - const assetData = whezData.unwrap(); - const whezAmount = assetData.balance.toString(); - whezBalance = formatBalance(whezAmount); - if (import.meta.env.DEV) console.log('✅ wHEZ balance found:', whezBalance); - } else { - if (import.meta.env.DEV) console.warn('⚠️ wHEZ asset not found for this account'); - } - } catch (err) { - if (import.meta.env.DEV) console.error('❌ Failed to fetch wHEZ balance:', err); - } - - // Fetch wUSDT (Asset ID: 2) - IMPORTANT: wUSDT has 6 decimals, not 12! - let wusdtBalance = '0'; - try { - const wusdtData = await pezkuwi.api.query.assets.account(ASSET_IDS.WUSDT, address); - if (import.meta.env.DEV) console.log('📊 Raw wUSDT data:', wusdtData.toHuman()); - - if (wusdtData.isSome) { - const assetData = wusdtData.unwrap(); - const wusdtAmount = assetData.balance.toString(); - wusdtBalance = formatBalance(wusdtAmount, 6); // wUSDT uses 6 decimals! - if (import.meta.env.DEV) console.log('✅ wUSDT balance found:', wusdtBalance); - } else { - if (import.meta.env.DEV) console.warn('⚠️ wUSDT asset not found for this account'); - } - } catch (err) { - if (import.meta.env.DEV) console.error('❌ Failed to fetch wUSDT balance:', err); - } - - setBalances({ - HEZ: hezBalance, - PEZ: pezBalance, - wHEZ: whezBalance, - USDT: wusdtBalance, - }); - - if (import.meta.env.DEV) console.log('✅ Balances updated:', { HEZ: hezBalance, PEZ: pezBalance, wHEZ: whezBalance, wUSDT: wusdtBalance }); - } catch (err) { - if (import.meta.env.DEV) console.error('Failed to fetch balances:', err); - setError('Failed to fetch balances'); - } - }, [pezkuwi.api, pezkuwi.isApiReady]); - - // Connect wallet (Pezkuwi.js extension) - const connectWallet = useCallback(async () => { - try { - setError(null); - await pezkuwi.connectWallet(); - } catch (err) { - if (import.meta.env.DEV) console.error('Wallet connection failed:', err); - const errorMessage = err instanceof Error ? err.message : WALLET_ERRORS.CONNECTION_FAILED; - setError(errorMessage); - } - }, [pezkuwi]); - - // Disconnect wallet - const disconnect = useCallback(() => { - pezkuwi.disconnectWallet(); - setBalance('0'); - setError(null); - }, [pezkuwi]); - - // Switch account - const switchAccount = useCallback((account: InjectedAccountWithMeta) => { - pezkuwi.setSelectedAccount(account); - }, [pezkuwi]); - - // Sign and submit transaction - const signTransaction = useCallback(async (tx: unknown): Promise => { - if (!pezkuwi.api || !pezkuwi.selectedAccount) { - throw new Error(WALLET_ERRORS.API_NOT_READY); - } - - try { - // Check if running in mobile app - use native bridge for signing - if (isMobileApp()) { - if (import.meta.env.DEV) console.log('[Mobile] Using native bridge for transaction signing'); - - // Extract transaction details from the tx object - const txAny = tx as { - method: { - section: string; - method: string; - args: unknown[]; - toHuman?: () => { args?: Record }; - }; - }; - - // Get section, method and args from the transaction - const section = txAny.method.section; - const method = txAny.method.method; - - // Extract args - convert to array format - const argsHuman = txAny.method.toHuman?.()?.args || {}; - const args = Object.values(argsHuman); - - if (import.meta.env.DEV) { - console.log('[Mobile] Transaction details:', { section, method, args }); - } - - const payload: TransactionPayload = { section, method, args }; - - // Sign and send via native bridge - const blockHash = await signTransactionNative(payload); - - if (import.meta.env.DEV) { - console.log('[Mobile] Transaction submitted, block hash:', blockHash); - } - - return blockHash; - } - - // Desktop: Use browser extension for signing - const { web3FromAddress } = await import('@pezkuwi/extension-dapp'); - const injector = await web3FromAddress(pezkuwi.selectedAccount.address); - - // Sign and send transaction - const hash = await (tx as { signAndSend: (address: string, options: { signer: unknown }) => Promise<{ toHex: () => string }> }).signAndSend( - pezkuwi.selectedAccount.address, - { signer: injector.signer } - ); - - return hash.toHex(); - } catch (error) { - if (import.meta.env.DEV) console.error('Transaction failed:', error); - throw new Error(error instanceof Error ? error.message : WALLET_ERRORS.TRANSACTION_FAILED); - } - }, [pezkuwi.api, pezkuwi.selectedAccount]); - - // Sign message - const signMessage = useCallback(async (message: string): Promise => { - if (!pezkuwi.selectedAccount) { - throw new Error('No account selected'); - } - - try { - const { web3FromAddress } = await import('@pezkuwi/extension-dapp'); - const injector = await web3FromAddress(pezkuwi.selectedAccount.address); - - if (!injector.signer.signRaw) { - throw new Error('Wallet does not support message signing'); - } - - const { signature } = await injector.signer.signRaw({ - address: pezkuwi.selectedAccount.address, - data: message, - type: 'bytes' - }); - - return signature; - } catch (error) { - if (import.meta.env.DEV) console.error('Message signing failed:', error); - throw new Error(error instanceof Error ? error.message : 'Failed to sign message'); - } - }, [pezkuwi.selectedAccount]); - - // Get signer from extension when account changes - useEffect(() => { - const getSigner = async () => { - if (pezkuwi.selectedAccount) { - try { - const injector = await web3FromAddress(pezkuwi.selectedAccount.address); - setSigner(injector.signer); - if (import.meta.env.DEV) console.log('✅ Signer obtained for', pezkuwi.selectedAccount.address); - } catch (error) { - if (import.meta.env.DEV) console.error('Failed to get signer:', error); - setSigner(null); - } - } else { - setSigner(null); - } - }; - - getSigner(); - }, [pezkuwi.selectedAccount]); - - // Update balance when selected account changes - useEffect(() => { - if (import.meta.env.DEV) console.log('🔄 WalletContext useEffect triggered!', { - hasAccount: !!pezkuwi.selectedAccount, - isApiReady: pezkuwi.isApiReady, - address: pezkuwi.selectedAccount?.address - }); - - if (pezkuwi.selectedAccount && pezkuwi.isApiReady) { - updateBalance(pezkuwi.selectedAccount.address); - } - }, [pezkuwi.selectedAccount, pezkuwi.isApiReady, updateBalance]); - - // Sync error state with PezkuwiContext - useEffect(() => { - if (pezkuwi.error) { - setError(pezkuwi.error); - } - }, [pezkuwi.error]); - - // Refresh balances for current account - const refreshBalances = useCallback(async () => { - if (pezkuwi.selectedAccount) { - await updateBalance(pezkuwi.selectedAccount.address); - } - }, [pezkuwi.selectedAccount, updateBalance]); +export function WalletProvider({ children }: { children: ReactNode }) { + const { user } = useAuth(); const value: WalletContextType = { - isConnected: pezkuwi.accounts.length > 0, - account: pezkuwi.selectedAccount?.address || null, - accounts: pezkuwi.accounts, - balance, - balances, - error: error || pezkuwi.error, - signer, - connectWallet, - disconnect, - switchAccount, - signTransaction, - signMessage, - refreshBalances, + address: user?.wallet_address || null, + isConnected: !!user?.wallet_address, }; return ( @@ -316,12 +27,12 @@ export const WalletProvider: React.FC<{ children: React.ReactNode }> = ({ childr {children} ); -}; +} -export const useWallet = () => { +export function useWallet() { const context = useContext(WalletContext); if (!context) { throw new Error('useWallet must be used within WalletProvider'); } return context; -}; \ No newline at end of file +} diff --git a/src/lib/ipfs.ts b/src/lib/ipfs.ts new file mode 100644 index 0000000..76d46fc --- /dev/null +++ b/src/lib/ipfs.ts @@ -0,0 +1,53 @@ +import { toast } from 'sonner'; + +// Helper to get environment variables that works in both web (Vite) and React Native (Expo) +const getEnv = (key: string): string | undefined => { + // Check for Vite environment (web) + if (typeof import.meta !== 'undefined' && import.meta.env) { + return (import.meta.env as any)[key]; + } + // Check for Expo environment (React Native) + if (typeof process !== 'undefined' && process.env) { + const expoKey = key.replace('VITE_', 'EXPO_PUBLIC_'); + return process.env[expoKey] || process.env[key]; + } + return undefined; +}; + +const PINATA_JWT = getEnv('VITE_PINATA_JWT'); +const PINATA_API = 'https://api.pinata.cloud/pinning/pinFileToIPFS'; + +export async function uploadToIPFS(file: File): Promise { + if (!PINATA_JWT || PINATA_JWT === 'your_pinata_jwt_here') { + throw new Error('Pinata JWT not configured. Set VITE_PINATA_JWT in .env'); + } + + const formData = new FormData(); + formData.append('file', file); + + try { + const response = await fetch(PINATA_API, { + method: 'POST', + headers: { + 'Authorization': `Bearer ${PINATA_JWT}`, + }, + body: formData, + }); + + if (!response.ok) { + const errorData = await response.json().catch(() => ({})); + throw new Error(errorData.error || `Upload failed: ${response.statusText}`); + } + + const data = await response.json(); + return data.IpfsHash; // Returns: Qm... + } catch (error) { + console.error('IPFS upload error:', error); + toast.error('Failed to upload to IPFS'); + throw error; + } +} + +export function getIPFSUrl(hash: string): string { + return `https://gateway.pinata.cloud/ipfs/${hash}`; +} diff --git a/src/lib/p2p-fiat.ts b/src/lib/p2p-fiat.ts new file mode 100644 index 0000000..d24a175 --- /dev/null +++ b/src/lib/p2p-fiat.ts @@ -0,0 +1,568 @@ +/** + * P2P Fiat Trading System - Mobile Version (OKX-Style Internal Ledger) + * + * @module p2p-fiat + * @description P2P fiat-to-crypto trading with internal ledger escrow + * + * Security Model: + * - Authentication via Telegram initData (cryptographically signed) + * - Blockchain transactions ONLY occur at deposit/withdraw (backend) + * - P2P trades use internal database balance transfers + * - No client-side blockchain transactions + */ + +import { toast } from 'sonner'; +import { supabase } from '@/lib/supabase'; + +// ===================================================== +// TYPES +// ===================================================== + +export type FiatCurrency = + | 'TRY' | 'IQD' | 'IRR' | 'EUR' | 'USD' | 'GBP' + | 'SEK' | 'CHF' | 'NOK' | 'DKK' | 'AUD' | 'CAD'; + +export type CryptoToken = 'HEZ' | 'PEZ'; +export type OfferStatus = 'open' | 'paused' | 'locked' | 'completed' | 'cancelled'; +export type TradeStatus = 'pending' | 'payment_sent' | 'completed' | 'cancelled' | 'disputed' | 'refunded'; + +export interface PaymentMethod { + id: string; + currency: FiatCurrency; + country: string; + method_name: string; + method_type: 'bank' | 'mobile_payment' | 'cash' | 'crypto_exchange'; + logo_url?: string; + fields: Record; + validation_rules: Record; + min_trade_amount: number; + max_trade_amount?: number; + processing_time_minutes: number; +} + +export interface ValidationRule { + pattern?: string; + minLength?: number; + maxLength?: number; + required?: boolean; +} + +export interface P2PFiatOffer { + id: string; + seller_id: string; + seller_wallet: string; + token: CryptoToken; + amount_crypto: number; + fiat_currency: FiatCurrency; + fiat_amount: number; + price_per_unit: number; + payment_method_id: string; + payment_details_encrypted: string; + min_order_amount?: number; + max_order_amount?: number; + time_limit_minutes: number; + auto_reply_message?: string; + min_buyer_completed_trades: number; + min_buyer_reputation: number; + status: OfferStatus; + remaining_amount: number; + created_at: string; + expires_at: string; +} + +export interface P2PFiatTrade { + id: string; + offer_id: string; + seller_id: string; + buyer_id: string; + buyer_wallet: string; + crypto_amount: number; + fiat_amount: number; + price_per_unit: number; + escrow_locked_amount: number; + buyer_marked_paid_at?: string; + buyer_payment_proof_url?: string; + seller_confirmed_at?: string; + status: TradeStatus; + payment_deadline: string; + confirmation_deadline?: string; + created_at: string; + completed_at?: string; +} + +export interface P2PReputation { + user_id: string; + total_trades: number; + completed_trades: number; + cancelled_trades: number; + disputed_trades: number; + reputation_score: number; + trust_level: 'new' | 'basic' | 'intermediate' | 'advanced' | 'verified'; + verified_merchant: boolean; + avg_payment_time_minutes?: number; +} + +export interface InternalBalance { + token: CryptoToken; + available_balance: number; + locked_balance: number; + total_balance: number; + total_deposited: number; + total_withdrawn: number; +} + +export interface DepositWithdrawRequest { + id: string; + user_id: string; + request_type: 'deposit' | 'withdraw'; + token: CryptoToken; + amount: number; + wallet_address: string; + blockchain_tx_hash?: string; + status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled'; + processed_at?: string; + error_message?: string; + created_at: string; +} + +// ===================================================== +// VALIDATION +// ===================================================== + +export interface ValidationRule { + pattern?: string; + minLength?: number; + maxLength?: number; + required?: boolean; +} + +/** + * Validate payment details against method rules + */ +export function validatePaymentDetails( + paymentDetails: Record, + validationRules: Record +): { valid: boolean; errors: Record } { + const errors: Record = {}; + + for (const [field, rules] of Object.entries(validationRules)) { + const value = paymentDetails[field] || ''; + + if (rules.required && !value) { + errors[field] = 'This field is required'; + continue; + } + + if (rules.pattern && value) { + const regex = new RegExp(rules.pattern); + if (!regex.test(value)) { + errors[field] = 'Invalid format'; + } + } + + if (rules.minLength && value.length < rules.minLength) { + errors[field] = `Minimum ${rules.minLength} characters`; + } + + if (rules.maxLength && value.length > rules.maxLength) { + errors[field] = `Maximum ${rules.maxLength} characters`; + } + } + + return { + valid: Object.keys(errors).length === 0, + errors + }; +} + +// ===================================================== +// PAYMENT METHODS +// ===================================================== + +export async function getPaymentMethods(currency: FiatCurrency): Promise { + try { + const { data, error } = await supabase + .from('payment_methods') + .select('*') + .eq('currency', currency) + .eq('is_active', true) + .order('display_order'); + + if (error) throw error; + return data || []; + } catch (error) { + console.error('Get payment methods error:', error); + return []; + } +} + +// ===================================================== +// OFFERS +// ===================================================== + +export async function getActiveOffers( + currency?: FiatCurrency, + token?: CryptoToken +): Promise { + try { + let query = supabase + .from('p2p_fiat_offers') + .select('*') + .eq('status', 'open') + .gt('remaining_amount', 0) + .gt('expires_at', new Date().toISOString()) + .order('price_per_unit'); + + if (currency) query = query.eq('fiat_currency', currency); + if (token) query = query.eq('token', token); + + const { data, error } = await query; + if (error) throw error; + + return data || []; + } catch (error) { + console.error('Get active offers error:', error); + return []; + } +} + +export async function acceptFiatOffer(params: { + offerId: string; + buyerWallet: string; + amount?: number; +}): Promise { + const { offerId, buyerWallet, amount } = params; + + try { + const { data: user } = await supabase.auth.getUser(); + if (!user.user) throw new Error('Not authenticated'); + + const { data: offer } = await supabase + .from('p2p_fiat_offers') + .select('remaining_amount, min_buyer_completed_trades, min_buyer_reputation') + .eq('id', offerId) + .single(); + + if (!offer) throw new Error('Offer not found'); + + const tradeAmount = amount || offer.remaining_amount; + + // Check reputation requirements + if (offer.min_buyer_completed_trades > 0 || offer.min_buyer_reputation > 0) { + const { data: reputation } = await supabase + .from('p2p_reputation') + .select('completed_trades, reputation_score') + .eq('user_id', user.user.id) + .single(); + + if (!reputation) { + throw new Error('Seller requires experienced buyers'); + } + if (reputation.completed_trades < offer.min_buyer_completed_trades) { + throw new Error(`Minimum ${offer.min_buyer_completed_trades} completed trades required`); + } + if (reputation.reputation_score < offer.min_buyer_reputation) { + throw new Error(`Minimum reputation score ${offer.min_buyer_reputation} required`); + } + } + + // Atomic accept + const { data: result, error: rpcError } = await supabase.rpc('accept_p2p_offer', { + p_offer_id: offerId, + p_buyer_id: user.user.id, + p_buyer_wallet: buyerWallet, + p_amount: tradeAmount + }); + + if (rpcError) throw rpcError; + + const response = typeof result === 'string' ? JSON.parse(result) : result; + + if (!response.success) { + throw new Error(response.error || 'Failed to accept offer'); + } + + toast.success('Trade started! Send payment within time limit.'); + return response.trade_id; + } catch (error: unknown) { + const message = error instanceof Error ? error.message : 'Failed to accept offer'; + toast.error(message); + throw error; + } +} + +// ===================================================== +// TRADES +// ===================================================== + +export async function getUserTrades(userId: string): Promise { + try { + const { data, error } = await supabase + .from('p2p_fiat_trades') + .select('*') + .or(`seller_id.eq.${userId},buyer_id.eq.${userId}`) + .order('created_at', { ascending: false }); + + if (error) throw error; + return data || []; + } catch (error) { + console.error('Get user trades error:', error); + return []; + } +} + +export async function getTradeById(tradeId: string): Promise { + try { + const { data, error } = await supabase + .from('p2p_fiat_trades') + .select('*') + .eq('id', tradeId) + .single(); + + if (error) throw error; + return data; + } catch (error) { + console.error('Get trade by ID error:', error); + return null; + } +} + +export async function markPaymentSent( + tradeId: string, + paymentProofUrl?: string +): Promise { + try { + const confirmationDeadline = new Date(Date.now() + 60 * 60 * 1000); // 60 min + + const { error } = await supabase + .from('p2p_fiat_trades') + .update({ + buyer_marked_paid_at: new Date().toISOString(), + buyer_payment_proof_url: paymentProofUrl, + status: 'payment_sent', + confirmation_deadline: confirmationDeadline.toISOString() + }) + .eq('id', tradeId); + + if (error) throw error; + toast.success('Payment marked as sent. Waiting for seller confirmation...'); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : 'Failed to mark payment'; + toast.error(message); + throw error; + } +} + +export async function confirmPaymentReceived(tradeId: string): Promise { + try { + const { data: userData } = await supabase.auth.getUser(); + const sellerId = userData.user?.id; + if (!sellerId) throw new Error('Not authenticated'); + + const { data: trade } = await supabase + .from('p2p_fiat_trades') + .select('*, p2p_fiat_offers(token)') + .eq('id', tradeId) + .single(); + + if (!trade) throw new Error('Trade not found'); + if (trade.seller_id !== sellerId) throw new Error('Only seller can confirm'); + if (trade.status !== 'payment_sent') throw new Error('Payment not marked as sent'); + + toast.info('Releasing crypto to buyer...'); + + // Release escrow internally + const { data: result, error: releaseError } = await supabase.rpc('release_escrow_internal', { + p_from_user_id: trade.seller_id, + p_to_user_id: trade.buyer_id, + p_token: trade.p2p_fiat_offers.token, + p_amount: trade.crypto_amount, + p_reference_type: 'trade', + p_reference_id: tradeId + }); + + if (releaseError) throw releaseError; + + const response = typeof result === 'string' ? JSON.parse(result) : result; + if (!response.success) throw new Error(response.error || 'Failed to release'); + + // Update trade status + await supabase + .from('p2p_fiat_trades') + .update({ + seller_confirmed_at: new Date().toISOString(), + status: 'completed', + completed_at: new Date().toISOString() + }) + .eq('id', tradeId); + + toast.success('Payment confirmed! Crypto released to buyer.'); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : 'Failed to confirm'; + toast.error(message); + throw error; + } +} + +export async function cancelTrade(tradeId: string, reason?: string): Promise { + try { + const { data: user } = await supabase.auth.getUser(); + if (!user.user) throw new Error('Not authenticated'); + + const { data: trade } = await supabase + .from('p2p_fiat_trades') + .select('*') + .eq('id', tradeId) + .single(); + + if (!trade) throw new Error('Trade not found'); + if (trade.status !== 'pending') throw new Error('Cannot cancel at this stage'); + + await supabase + .from('p2p_fiat_trades') + .update({ + status: 'cancelled', + cancelled_by: user.user.id, + cancel_reason: reason + }) + .eq('id', tradeId); + + // Restore offer amount + const { data: offer } = await supabase + .from('p2p_fiat_offers') + .select('remaining_amount') + .eq('id', trade.offer_id) + .single(); + + if (offer) { + await supabase + .from('p2p_fiat_offers') + .update({ + remaining_amount: offer.remaining_amount + trade.crypto_amount, + status: 'open' + }) + .eq('id', trade.offer_id); + } + + toast.success('Trade cancelled'); + } catch (error: unknown) { + const message = error instanceof Error ? error.message : 'Failed to cancel'; + toast.error(message); + throw error; + } +} + +// ===================================================== +// REPUTATION +// ===================================================== + +export async function getUserReputation(userId: string): Promise { + try { + const { data, error } = await supabase + .from('p2p_reputation') + .select('*') + .eq('user_id', userId) + .single(); + + if (error && error.code !== 'PGRST116') throw error; + return data; + } catch (error) { + console.error('Get reputation error:', error); + return null; + } +} + +// ===================================================== +// INTERNAL BALANCE (OKX-Style) +// ===================================================== + +export async function getInternalBalances(): Promise { + try { + const { data: userData } = await supabase.auth.getUser(); + const userId = userData.user?.id; + if (!userId) return []; + + const { data, error } = await supabase.rpc('get_user_internal_balance', { + p_user_id: userId + }); + + if (error) throw error; + return typeof data === 'string' ? JSON.parse(data) : (data || []); + } catch (error) { + console.error('Get balances error:', error); + return []; + } +} + +export async function getInternalBalance(token: CryptoToken): Promise { + const balances = await getInternalBalances(); + return balances.find(b => b.token === token) || null; +} + +export async function requestWithdraw( + token: CryptoToken, + amount: number, + walletAddress: string +): Promise { + try { + const { data: userData } = await supabase.auth.getUser(); + const userId = userData.user?.id; + if (!userId) throw new Error('Not authenticated'); + + if (amount <= 0) throw new Error('Amount must be greater than 0'); + if (!walletAddress || walletAddress.length < 40) throw new Error('Invalid wallet address'); + + toast.info('Processing withdrawal request...'); + + const { data, error } = await supabase.rpc('request_withdraw', { + p_user_id: userId, + p_token: token, + p_amount: amount, + p_wallet_address: walletAddress + }); + + if (error) throw error; + + const result = typeof data === 'string' ? JSON.parse(data) : data; + if (!result.success) throw new Error(result.error || 'Request failed'); + + toast.success(`Withdrawal request submitted! ${amount} ${token} will be sent.`); + return result.request_id; + } catch (error: unknown) { + const message = error instanceof Error ? error.message : 'Request failed'; + toast.error(message); + throw error; + } +} + +export async function getDepositWithdrawHistory(): Promise { + try { + const { data, error } = await supabase + .from('p2p_deposit_withdraw_requests') + .select('*') + .order('created_at', { ascending: false }) + .limit(50); + + if (error) throw error; + return data || []; + } catch (error) { + console.error('Get history error:', error); + return []; + } +} + +export async function getPlatformWalletAddress(): Promise { + const DEFAULT_ADDRESS = '5DFwqK698vL4gXHEcanaewnAqhxJ2rjhAogpSTHw3iwGDwd3'; + try { + const { data, error } = await supabase + .from('p2p_config') + .select('value') + .eq('key', 'platform_escrow_wallet') + .single(); + + if (error) return DEFAULT_ADDRESS; + return data?.value || DEFAULT_ADDRESS; + } catch { + return DEFAULT_ADDRESS; + } +} diff --git a/src/lib/supabase.ts b/src/lib/supabase.ts index c2701cf..9455897 100644 --- a/src/lib/supabase.ts +++ b/src/lib/supabase.ts @@ -10,4 +10,4 @@ if (!supabaseUrl || !supabaseKey) { const supabase = createClient(supabaseUrl, supabaseKey); -export { supabase }; \ No newline at end of file +export { supabase }; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 7edb33b..5e75029 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -2,13 +2,18 @@ import { clsx, type ClassValue } from "clsx" import { twMerge } from "tailwind-merge" /** - * Web-specific className utility (uses Tailwind merge) + * Utility function to merge class names with Tailwind */ export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } /** - * Re-export formatNumber from shared utils + * Format a number with locale-specific formatting */ -export { formatNumber } from '@pezkuwi/utils/format'; +export function formatNumber(value: number, decimals: number = 2): string { + return value.toLocaleString(undefined, { + minimumFractionDigits: 0, + maximumFractionDigits: decimals + }); +} diff --git a/tsconfig.app.json b/tsconfig.app.json index a9b5a59..ff6d4bd 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -5,9 +5,15 @@ "useDefineForClassFields": true, "lib": ["ES2022", "DOM", "DOM.Iterable"], "module": "ESNext", - "types": ["vite/client"], + "types": ["vite/client", "node"], "skipLibCheck": true, + /* Path alias */ + "baseUrl": ".", + "paths": { + "@/*": ["./src/*"] + }, + /* Bundler mode */ "moduleResolution": "bundler", "allowImportingTsExtensions": true, diff --git a/vite.config.ts b/vite.config.ts index 8b0f57b..78778a3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,7 +1,14 @@ import { defineConfig } from 'vite' -import react from '@vitejs/plugin-react' +import react from '@vitejs/plugin-react-swc' +import path from 'path' // https://vite.dev/config/ export default defineConfig({ plugins: [react()], + base: '/p2p/', + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, })