diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..ef1008d --- /dev/null +++ b/.env.example @@ -0,0 +1,9 @@ +# MongoDB Atlas Connection String +# Replace with your actual MongoDB Atlas connection string +MONGODB_URI=mongodb+srv://:@/campusgigs?retryWrites=true&w=majority + +# Super Admin Configuration +SUPER_ADMIN_EMAIL=superadmin@campusgigs.edu +SUPER_ADMIN_PASSWORD=SuperAdminPass2026! +JWT_SECRET=campusgigs-super-secret-jwt-key-2026 + diff --git a/.vscode/settings.json b/.vscode/settings.json index 70dd163..b001961 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,35 +1,35 @@ { - "files.watcherExclude": { - "**/routeTree.gen.ts": true - }, - "search.exclude": { - "**/routeTree.gen.ts": true - }, - "files.readonlyInclude": { - "**/routeTree.gen.ts": true - }, - "[javascript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[javascriptreact]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[typescript]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[typescriptreact]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[json]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[jsonc]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "[css]": { - "editor.defaultFormatter": "biomejs.biome" - }, - "editor.codeActionsOnSave": { - "source.organizeImports.biome": "explicit" - } + "files.watcherExclude": { + "**/routeTree.gen.ts": true + }, + "search.exclude": { + "**/routeTree.gen.ts": true + }, + "files.readonlyInclude": { + "**/routeTree.gen.ts": true + }, + "[javascript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[javascriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescript]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[typescriptreact]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[json]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[jsonc]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "[css]": { + "editor.defaultFormatter": "biomejs.biome" + }, + "editor.codeActionsOnSave": { + "source.organizeImports.biome": "explicit" + } } diff --git a/biome.json b/biome.json index 64b4d31..033c04c 100644 --- a/biome.json +++ b/biome.json @@ -1,5 +1,5 @@ { - "$schema": "https://biomejs.dev/schemas/2.2.4/schema.json", + "$schema": "https://biomejs.dev/schemas/2.4.5/schema.json", "vcs": { "enabled": false, "clientKind": "git", @@ -33,4 +33,3 @@ } } } - diff --git a/bun.lock b/bun.lock index 85c59fd..2bd684d 100644 --- a/bun.lock +++ b/bun.lock @@ -10,10 +10,15 @@ "@tanstack/react-router": "1.170.46", "@tanstack/react-router-devtools": "1.167.3", "@tanstack/react-start": "1.169.5", + "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cn": "^0.5.2", + "jose": "^6.2.17", "lucide-react": "^0.577.0", + "mongoose": "^9.12.1", "nitro": "3.0.260610-beta", + "radix-ui": "^1.8.1", "react": "^19.2.0", "react-dom": "^19.2.0", "tailwind-merge": "^3.0.2", @@ -24,6 +29,7 @@ "@biomejs/biome": "2.4.5", "@tailwindcss/typography": "^0.5.16", "@tanstack/devtools-vite": "0.8.6", + "@types/bcryptjs": "^2.4.6", "@types/node": "^22.10.2", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", @@ -90,6 +96,14 @@ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@2.2.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-JP7OYH38Q7+pyTAe14raZHLQIK7BDKbdwji+Xi3pcd/6QkJYQH3SX4wu6EplGrGJWRepoNzedfm0G3B9Z4e9Rw=="], + "@floating-ui/core": ["@floating-ui/core@1.8.0", "", { "dependencies": { "@floating-ui/utils": "^0.2.12" } }, "sha512-0CIZ5itps/8x7BG8dEIhs53BvCUH2PCoogtakwRTut+Arm58sJooJ0AuZhLw2HJYIR5cMLNPBSS728sPho2khQ=="], + + "@floating-ui/dom": ["@floating-ui/dom@1.8.0", "", { "dependencies": { "@floating-ui/core": "^1.8.0", "@floating-ui/utils": "^0.2.12" } }, "sha512-yXSrzeHZBTZadLOlfyhCkJHNeLJnHRnRInwdZ40L7ZiaAtrBwoYlsDrX3v5zB1Utk7CLfzcOVnVVWoXEky7Ceg=="], + + "@floating-ui/react-dom": ["@floating-ui/react-dom@2.1.9", "", { "dependencies": { "@floating-ui/dom": "^1.8.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-JDjEFGCpImxDCA7JJKviA0M9+RtmJdj0m/NVU5IMgBK+AmZouAQQ7/+2GLH0GXXY0YMw9oXPB8hKdbPYg5QLYg=="], + + "@floating-ui/utils": ["@floating-ui/utils@0.2.12", "", {}, "sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -100,6 +114,8 @@ "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + "@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.6.3", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-aHGFvT95c82CoQ7hb6qXpz8Hcc5PJb3ZtQ+P01QDBbyQday0mjcrJPeNJOHCgWe0pGX0zKmKP+1mWi/I/BCX9Q=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.2.6", "", { "dependencies": { "@tybys/wasm-util": "^0.10.3" }, "peerDependencies": { "@emnapi/core": "^1.7.1 || 2.0.0-beta.2", "@emnapi/runtime": "^1.7.1 || 2.0.0-beta.2" } }, "sha512-mRyDmEw9/fCDXCGfL1sbgIw/GUK0PVxRW4YwgPN61F3LQ+fSyoBFarJVzgGyhF6kRzEQZ0FejDqm4WLXSeqQmw=="], "@neodrag/core": ["@neodrag/core@3.0.0-next.11", "", {}, "sha512-3WQWxyrbxiaK9zS5JU2wJsW2gpoQlZBXVghduBh61JpqaeE0T0cte8R0qYK2RuJo3J2TYQYqxO19CpG/C1i5eg=="], @@ -156,6 +172,126 @@ "@oxc-project/types": ["@oxc-project/types@0.156.0", "", {}, "sha512-1Duyg48TdJNdyhEB8Xn1L36bT9Pv6GUtlKjHxOys8xkaF/rQq3XJE+bAurSK/AgIz5mGFsb77cn9Pb20+WCScw=="], + "@radix-ui/number": ["@radix-ui/number@1.1.3", "", {}, "sha512-Road2bidD0uu/1BGDOWNdPI06g0lIRy6IF9GZcIrDK2KGItfor8IQwQa+yM2ERgHM1MmHxaxpTzk0/Jp42lNfA=="], + + "@radix-ui/primitive": ["@radix-ui/primitive@1.2.0", "", {}, "sha512-KABY+VMIRPJL+Vyzv/335FEhXXD/xDv8BMb1kl48/aXJZMH0LtHCTx5ga5RnxaEP3nZHtmTLf1P9vanmFX46bw=="], + + "@radix-ui/react-accessible-icon": ["@radix-ui/react-accessible-icon@1.2.0", "", { "dependencies": { "@radix-ui/react-visually-hidden": "1.2.13" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-BB4+HHkFl3HipEAhm/mLhrpXV+2kRX7MvY4AaImUcQVeWQt5KhV1JEByNs8NGfJOcr95RcQGijt9+OrCo68xGA=="], + + "@radix-ui/react-accordion": ["@radix-ui/react-accordion@1.3.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collapsible": "1.2.1", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-lCZ6sbGS/7WO0j84yx4srIgOf0MRJ/sZ13RonADXxvKBym8xrxNCq5f6DibGcr38sIlwPk8/52uYdXeOPsbEDw=="], + + "@radix-ui/react-alert-dialog": ["@radix-ui/react-alert-dialog@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-dialog": "1.3.1", "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-MWC4N7RY+qsbNrFNo7yQ9ZbdoZEW+B5DtpWxJfUnVCqcQxxqqg3qis3RMPTDbCNaTIdHJSn5XDkl9ZdVWXknLg=="], + + "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.17", "", { "dependencies": { "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-67jjcVvxjXCCStnTW3Z+M7jmaFMuMR7M2JgpDzH4w7JSRrikmzhjfYgP/09ECb8n7whnsgh1ifJ+Ie96qEj+pQ=="], + + "@radix-ui/react-aspect-ratio": ["@radix-ui/react-aspect-ratio@1.2.0", "", { "dependencies": { "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-gHEU+Lx8DlT/FYIjZQvvBJrFSwUngXTjh7FeL9QvJaNALDLez6pgDj2axCUihBjtPBy82LN0gNb25+sTcSXNmA=="], + + "@radix-ui/react-avatar": ["@radix-ui/react-avatar@1.3.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ssZOkfQNkFpYVmCPQlr978xSaU7moSrGXdwg4j2Xs2WlGl/1KsSZtfpK+WnLnx0Rwll1BAh6oUrAy/uR31SBvQ=="], + + "@radix-ui/react-checkbox": ["@radix-ui/react-checkbox@1.4.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-size": "1.1.5" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-KWtwhh+98ML3seZ9vO9yRY2OwaQG90dCiDAxjpSNY2AaZ3Hik3B0dsk6m0ILywo/no3A9/SozadyPMNweVT8yw=="], + + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-fSzLb/qy1wuZSxQBcxpW0hNXYHDMWiIXDGyJGdA4+FSLknOWZsNRrTCUavxb4PI8b8lvHo/CysO6NVi5zLpo5g=="], + + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.2.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+yiQzqcc7Sy7/5s0jrMCjpPRFB3538VNrXPWdTLZhE/SCfgEws4aI6lHWN/vvcnyhHndROWBzqmqB4QBrITZ9Q=="], + + "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.5", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-+48PbAAbq3didjJxa+OaWY2ZwgAKsNiRGyeHKszblZMQ+kcpd9pAaT11cMkGEie0vsOi3QdeTE6d5Fe3Gn61kA=="], + + "@radix-ui/react-context": ["@radix-ui/react-context@1.3.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-v5AYEocMISkbJ+1fTXzZpiFv7NRr3D2WiYbLIKf+FkmJ8VkP6d503eV2n34MFussHXY1pcoq3yfJiQH77dc4Mg=="], + + "@radix-ui/react-context-menu": ["@radix-ui/react-context-menu@2.4.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-menu": "2.2.1", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Pddn3Z8lio3/ChxyXaEP3jHTTo1LTHCWwF8+mG1chQlWijP+ttVHBKwezrQTiV6Mj670ojvumqvuGh+mdv33iQ=="], + + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.3.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-focus-guards": "1.2.0", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+0E4qvVTwjAWra9sEhJ+9BTsvHE00zkUuNw1VGh7S+Gc13dfYk+S1PVQdchWDj+XYHhAai7Z7IpzXVDCGsDplg=="], + + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-FtRA52a/r+LKStFT+neLUm+8vSe7wbzkbQGGeU8ixFiwC8NU14Evoq9ATECqL3eGbcUzJR4fyfA4KkK/q6dfqg=="], + + "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-effect-event": "0.0.5" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-KIfDcAsx+oVylFZ4xryVKusJUrLLhTQmfnJW4kwk4Km5myvhHCrj04hnWWUC166+xD4DHdH8tQGylwYX1h6+Pg=="], + + "@radix-ui/react-dropdown-menu": ["@radix-ui/react-dropdown-menu@2.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-menu": "2.2.1", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XsHcmYG2AcqnvTqrOXXWcMrhquEIlqucizuDH1MV/qMRW+DndpBCMvNeTRXOxqmG1H5j89M/+I0/34Zdd+zDcQ=="], + + "@radix-ui/react-focus-guards": ["@radix-ui/react-focus-guards@1.2.0", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-MPm5R0iRGUDrDnPNxmLBkv+X7pN4XKzt+MbWJEuJQsH3NFH4omrbpXpucyYCCKTSt+na/ZxGhEWQrWTZ0285cA=="], + + "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.3.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-callback-ref": "1.1.5" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ugeMhbXt4y5S2T1GnIqbWDKEN962PDwnX1fN0eKOOgf5f63Dh2mn8F1FUySlgW0iF6GiVsRF32DzuDIWTUyQJg=="], + + "@radix-ui/react-form": ["@radix-ui/react-form@0.3.0", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-label": "2.1.17", "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-KnmrCM27m7Jo1R8S6cFDPrEeq20Jfn5dqde0SZrJr2lVXlj6/QZSUCyFoYh1d8ti0uUrMNK5Lx6dUDuINT+f4Q=="], + + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-popper": "1.4.0", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-4QooHxgmpCKRjj1Pft+hETKidYcrbPKcVwMGtk2Oa5DcWY2IH1fHuQt9LG6q5hUQBBV2MhQxSvtx6B/C1xfiTw=="], + + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.4", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-TMQp2llA+RYn7JcjnrMnz7wN4pcVttPZnRZo52PLQsoLVKzNlVwUeHmfePgTgRluXFvlD3GD5g5MOVVTJCO0qA=="], + + "@radix-ui/react-label": ["@radix-ui/react-label@2.1.17", "", { "dependencies": { "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-xmj7MN/drx1oUAXkfiFIvyDmGNUIN/omGobvA5oAoNqu9zO2MkISYGZOkBcqSzs/UZXXwV+/3ACRE7q7mGcmkA=="], + + "@radix-ui/react-menu": ["@radix-ui/react-menu@2.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-focus-guards": "1.2.0", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.4.0", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-callback-ref": "1.1.5", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-G34BsU90SBj5foYpz8pQqq/Q0Dlfyyl6KitWEfRwXC0QvfwhWNAwS49DWP2+rRJ0iwv20Z2Qx7Sk+T03bGo5JA=="], + + "@radix-ui/react-menubar": ["@radix-ui/react-menubar@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-menu": "2.2.1", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-bRTYtS9JITV2dUYqYZb7+N5SooQBkZrNRejfmyrSKe3Y57aXhKgx//kBUP0kY7pEpsWR/U1AHsAJRzjwCrK3PA=="], + + "@radix-ui/react-navigation-menu": ["@radix-ui/react-navigation-menu@1.4.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-previous": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.13" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-XPPZjkeyCSMNNvt0FyDv/PUauG7Ow4GwHQ/XZSwW36F/TS7rR4HFTsXH8ewLnXTOLn3x96/pmTPNQIVoTX/86g=="], + + "@radix-ui/react-one-time-password-field": ["@radix-ui/react-one-time-password-field@0.2.1", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-KtHdcgnaAK6rohQ/MMJoGYcbgSRJsIuGBdHrFeTB1/9SaKlDs14oSOniW6BL6M43HqvgCO3u4WuVdFLkuc6sqw=="], + + "@radix-ui/react-password-toggle-field": ["@radix-ui/react-password-toggle-field@0.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-is-hydrated": "0.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-vu4+ZoJAaFjdI7l1lDSZRs+4dEv83Jmu7lyzajPrAeQzX74y84n0rUNWbWSgN9XB5NE8IY86Z02kGH6oRrzR+w=="], + + "@radix-ui/react-popover": ["@radix-ui/react-popover@1.3.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-focus-guards": "1.2.0", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.4.0", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.1.4", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-ZNxvXRx5W2Wos4fXCXb5lpdqN0Z5chf+mStOUOTr4ik7d+62MRvoe9rzCA/eGNg36lxXqSrKEKmwuZEcUrIstw=="], + + "@radix-ui/react-popper": ["@radix-ui/react-popper@1.4.0", "", { "dependencies": { "@floating-ui/react-dom": "^2.0.0", "@radix-ui/react-arrow": "1.1.17", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-rect": "1.1.4", "@radix-ui/react-use-size": "1.1.5", "@radix-ui/rect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-yCWRvLeGv9Pz3+efwHqGPp70mZ56r3kfIgQVF56Oj2aAa4uP/Tb+IE59wNcc+758rctqbCKx4LbK8II/r7Bn0Q=="], + + "@radix-ui/react-portal": ["@radix-ui/react-portal@1.2.0", "", { "dependencies": { "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-StX7xXnaRU41VvFtMm9zlhVGz8XF6GmWWTXftkQaY+tqVkbs/R9UmAp5mLzHHapk5hM9RMZQvO/3usyIP/g42g=="], + + "@radix-ui/react-presence": ["@radix-ui/react-presence@1.1.11", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Z7/atlkab36Z8hm+V8zYc79r0W+tiOB1o0tun1By94JyadkekLQmtEAnBSP9XKXBNQsStoStS4+gX10JpS5akA=="], + + "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.2.0", "", { "dependencies": { "@radix-ui/react-slot": "1.5.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-HCTSPMNqv55Qk0IX4geJbpyXazmqulcimwuyjxTkKi29v+QZbG2Zho9BnbBq4YS2PF+KlNb2CXoLz66uUmblkg=="], + + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.2.1", "", { "dependencies": { "@radix-ui/react-context": "1.3.0", "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-yQBiM6J3YM8Ums1orkEFpbznK47geKxa0igikzcaS1xHgdba3DdAQBsGbCTt5BVfR8zhEvgxQ0poGcINaD3lvw=="], + + "@radix-ui/react-radio-group": ["@radix-ui/react-radio-group@1.5.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-size": "1.1.5" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-QbyVT1ddnjqB9H+CUYKkNWdU9fIK3QHCYtjf7mS6ekSqiuXkWtWn5UZW9mhA8qtl3vrVyIsmR9gePstAtKrqkw=="], + + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-UwraORzVUeEPMclhO1p0vz0qI+TbdDLO9i+FiOqhJyHKwdpCRCck9yVEscUnAMIzCiuNoQa8D4A9bIpOPXGt6Q=="], + + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.4.0", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-layout-effect": "1.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-CFrLqHNejWq5FhKAcj9V/WPGRsol76HBQto81NdOBVFEOqIEkJkKIJt0Z824A/9hHLCtoiR/Kw3euRgzXP2pxg=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.4.1", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-focus-guards": "1.2.0", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.4.0", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.13", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.7.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-888ZwW0Md6GyptuqYABRX9nLKgoYfcooCyBBvVaX5Jp3e+8Jp2i4Zo/Uy50Hm+Ysn2JfloQXstW4U36w8teVtg=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.2.0", "", { "dependencies": { "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-1jUAap0wZEqVP05Ou7kJgRxe/W+Qftng+rUNWGa0qkKbv9r5pWTG3E7a3ELKo2eJoHKMECnzuYrZpSyO1YgcYQ=="], + + "@radix-ui/react-slider": ["@radix-ui/react-slider@1.6.1", "", { "dependencies": { "@radix-ui/number": "1.1.3", "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-size": "1.1.5" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-IRNRR90moPqxfwCJ0XbMJIxo70W0/1ZExT/sNCxEHMaPx0SF9HoQrcPEC75+H28cFKa0b+KH63anfLYJmwKacw=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.5.0", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-tyYd9ddPXAysAbdbD9eYxgwpCSv6f3PncqVSNKQFSR5lcJVLz3cb/pANDoYrLiHBpXv7skortyUgGUWsYuPPFQ=="], + + "@radix-ui/react-switch": ["@radix-ui/react-switch@1.4.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-size": "1.1.5" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NpBGwc40rY02ZmSvuyeKk0Bvc8ZsNeTMi4qHvJ+ZH4EiYCv0zIXEgdCW4bVdzgIUeP9xxbLJSxKOUfqITYNmkA=="], + + "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-hRoJlode+hg5ZNlR+3tkfkUliIybdTpD5lkAvHQ9yaXseGf7XVO9Ib2GPEIOuaJJbo7YWYbNpDDRfo8Dhf6/xA=="], + + "@radix-ui/react-toast": ["@radix-ui/react-toast@1.3.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.13" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-iQvL0J7XK4HtJNGkkWMyYrboNseI7vtotFeUkIno8YNxVA9oM7B6//fh0Mc6TSKI96+zioLWi98lQLso88yJmQ=="], + + "@radix-ui/react-toggle": ["@radix-ui/react-toggle@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+3MUq+WyiBplXYqQ0EIYlq8R4sLQ0T5NbOK7DMdDLCKO93pe2uE0DUJTWWfTBprpJJXxfoF3eMW0xpakVZrYsg=="], + + "@radix-ui/react-toggle-group": ["@radix-ui/react-toggle-group@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-toggle": "1.2.1", "@radix-ui/react-use-controllable-state": "1.3.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-O8Ldx5ao4p4KoW+DO8/PmkOe5mdKNi3F2SseqjVj88wvDGDHe60cUmO0Zm/uHsKrvNgQGV8Efpuh95Tht/h4vw=="], + + "@radix-ui/react-toolbar": ["@radix-ui/react-toolbar@1.2.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-separator": "1.2.0", "@radix-ui/react-toggle-group": "1.2.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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Cm8m7JTEeUdbfA6T9xgyu8HfOpyvSx8pB/wj0DlEuWaJe9FilIs4Az9wci/lU9RbthV/UkltszMw5WnnD+m/Ug=="], + + "@radix-ui/react-tooltip": ["@radix-ui/react-tooltip@1.4.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-id": "1.1.4", "@radix-ui/react-popper": "1.4.0", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-visually-hidden": "1.2.13" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-NxLFGSFtZ78zVfcoOhTwR1xwqo5qx1Jh8Pw6FMZ9McCaqfRzz7/gIRahs2Oy5XlPgQ+8QkUcKbN5B0U44J/kCg=="], + + "@radix-ui/react-use-callback-ref": ["@radix-ui/react-use-callback-ref@1.1.5", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Y5GBf4NKeADoXI1geiMlMuMoURT3k9MizzzKhGb/m55H94aGigZ01PIpkvsO6lML13BA6XYAXSJ6gza7vTn5nw=="], + + "@radix-ui/react-use-controllable-state": ["@radix-ui/react-use-controllable-state@1.3.0", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-TAcBI7bw2ljYCbv4+zuTSd6zICu8pRt6SDN2vJ/Z06K2MXh6I9WYFe/A9ovlFDtR5HVHGm5iDxDoABm/++dbKg=="], + + "@radix-ui/react-use-effect-event": ["@radix-ui/react-use-effect-event@0.0.5", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-7cshFL8HGS/7HEiHH+9kL9HBwp2sa9yX18Knwek6KYWmXwM7pegMgta2AXMQKI+rq3JnfSj9x8wYqFMTdG1Jgg=="], + + "@radix-ui/react-use-escape-keydown": ["@radix-ui/react-use-escape-keydown@1.1.6", "", { "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.5" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-9cRullnQRv+kqnRmFlmh0WC3kAPuGmHiQ9VwsXdrYk//sBhgCKEttcpPEPgIQbyx6TGUmooVEHYTyohwz/lSJg=="], + + "@radix-ui/react-use-is-hydrated": ["@radix-ui/react-use-is-hydrated@0.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-umO/aJ+82CpOnhDZUTbILCQf7kU/g0iv+oGs/Q8jw7IkhWBzaEP4sA268PhFAJTFetbwp3ICc6ktpI4TqtxcIw=="], + + "@radix-ui/react-use-layout-effect": ["@radix-ui/react-use-layout-effect@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-K20DkRkUwDnxEYMBPcg3Y6voLkEy5p5QQmszZgLngKKiC7dzBR/aEuK3w1qlx2JWDUNH6FluahYdgR3BP+QbYw=="], + + "@radix-ui/react-use-previous": ["@radix-ui/react-use-previous@1.1.4", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-XoSLhbRbqxFtgJoi2fNHA3C6pDlY34x508vUpUGoFZfvePfHXHbE1lC4FYFMnJWgiCRroSTw6fOsXQoVS9RwZg=="], + + "@radix-ui/react-use-rect": ["@radix-ui/react-use-rect@1.1.4", "", { "dependencies": { "@radix-ui/rect": "1.1.3" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-cSOCh6JlkmfjLyNcLiu2nB4v+nm+dkZ+Q5KHWk/soo4U7ZLiEQFKHK9/YmtBHjfCEaU43IBKQOc4/uJmCaiCTQ=="], + + "@radix-ui/react-use-size": ["@radix-ui/react-use-size@1.1.5", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.4" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-V1McDQkMGzgivwqdu7u96APs7oi0Jvj9LTzgq3WWrDZqxWdAqU9yZnZQqk5OizGFR8badUQwZsrlao7MOlWYOA=="], + + "@radix-ui/react-visually-hidden": ["@radix-ui/react-visually-hidden@1.2.13", "", { "dependencies": { "@radix-ui/react-primitive": "2.2.0" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-feijiN1oPk4rck4u9Dm9tsU4mT6Wp9oeEEhaz1WUEs1VuqLf8xzsFG+Qk+uWq6TnMH9O+WRECRg0NftyP9E4Fw=="], + + "@radix-ui/rect": ["@radix-ui/rect@1.1.3", "", {}, "sha512-JtyZR+mqgBibTo8xea3B6ZRmzZiM/YeVBtUkas6zMuXjAlfIFIW2FgqeM9eLyvEaYX66vr6DJMK+4U6LV0KhNw=="], + "@rolldown/binding-android-arm-eabi": ["@rolldown/binding-android-arm-eabi@1.2.19", "", { "os": "android", "cpu": "arm" }, "sha512-e+Ebe0WT+9tbXmNKPIiC8YrWAHYwug3m6i4p3rZGGulfZri1jtI4HslT+OZnFf/OIHowWF71DTlHwXBP2SiA7A=="], "@rolldown/binding-android-arm64": ["@rolldown/binding-android-arm64@1.2.19", "", { "os": "android", "cpu": "arm64" }, "sha512-hOombrTJ+szt2ciEzS7oCA78lMPBgF5UMQWCJIyNfnrI7GLl8HYoFqCnpL2gcPOaXMqryGCU5ExdQKyDDcC+yA=="], @@ -202,6 +338,8 @@ "@solid-primitives/utils": ["@solid-primitives/utils@6.4.1", "", { "peerDependencies": { "solid-js": "^1.6.12" } }, "sha512-ISSB5QX1qP2ynrheIpYwc4oKR5Ny4siNuUyf1qZniy+Il+p/PtDB0QK1Dnle8noiHpwRD3gpPdubOC3qI/Zamg=="], + "@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], + "@tailwindcss/node": ["@tailwindcss/node@4.3.5", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.24.5", "jiti": "^2.7.0", "lightningcss": "1.33.0", "magic-string": "^1.1.0", "source-map-js": "^1.2.1", "tailwindcss": "4.3.5" } }, "sha512-yT53FzV4ke0cr/BguDxhWMpm5MJu1O2muzk+tKw6lKHjODAsYArBbWwFbJiaksfzFv1tpkO9QHGzmfs1hJT66g=="], "@tailwindcss/oxide": ["@tailwindcss/oxide@4.3.5", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.3.5", "@tailwindcss/oxide-darwin-arm64": "4.3.5", "@tailwindcss/oxide-darwin-x64": "4.3.5", "@tailwindcss/oxide-freebsd-x64": "4.3.5", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.5", "@tailwindcss/oxide-linux-arm64-gnu": "4.3.5", "@tailwindcss/oxide-linux-arm64-musl": "4.3.5", "@tailwindcss/oxide-linux-x64-gnu": "4.3.5", "@tailwindcss/oxide-linux-x64-musl": "4.3.5", "@tailwindcss/oxide-wasm32-wasi": "4.3.5", "@tailwindcss/oxide-win32-arm64-msvc": "4.3.5", "@tailwindcss/oxide-win32-x64-msvc": "4.3.5" } }, "sha512-PynqGLZsZvwM9tqD/CUxkOIfyd6K5elZ2MooauzJryP/UqXYgi3yYbtt3N0Xe+MHV/eFKPFQCEfIKAqlAS1CBg=="], @@ -292,24 +430,36 @@ "@tybys/wasm-util": ["@tybys/wasm-util@0.10.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A=="], + "@types/bcryptjs": ["@types/bcryptjs@2.4.6", "", {}, "sha512-9xlo6R2qDs5uixm0bcIqCeMCE6HiQsIyel9KQySStiyqNl2tnj2mP3DX1Nf56MD6KMenNNlBBsy3LJ7gUEQPXQ=="], + "@types/node": ["@types/node@22.20.5", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA=="], "@types/react": ["@types/react@19.3.0", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg=="], "@types/react-dom": ["@types/react-dom@19.3.0", "", { "peerDependencies": { "@types/react": "^19.3.0" } }, "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q=="], + "@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="], + + "@types/whatwg-url": ["@types/whatwg-url@13.0.0", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@6.3.0", "", { "dependencies": { "@rolldown/pluginutils": "^1.0.1" }, "peerDependencies": { "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0 || ^0.3.0", "babel-plugin-react-compiler": "^1.0.0", "oxc-transform-react": ">=0.152.0 <0.157.0", "vite": "^8.0.0" }, "optionalPeers": ["@rolldown/plugin-babel", "babel-plugin-react-compiler", "oxc-transform-react"] }, "sha512-H9E955aVvD2F4zNHCw4eIZbIfF6xKldlh7MNu+hSU7xwQzh65lI/N+CJWqmfhi0PEjDXIt/H7T7J/4wU63KJiw=="], "ansis": ["ansis@4.5.0", "", {}, "sha512-KwIVkfFfJSSzBmZhDGuTmkhWqTdyB5oNVzzznBJbBZ5bZtHiqSrcugRF0RKJKT4Ss3d7ZrcjnlFVDlQNmtWfWw=="], "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], + "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], + "babel-dead-code-elimination": ["babel-dead-code-elimination@1.0.12", "", { "dependencies": { "@babel/core": "^7.23.7", "@babel/parser": "^7.23.6", "@babel/traverse": "^7.23.7", "@babel/types": "^7.23.6" } }, "sha512-GERT7L2TiYcYDtYk1IpD+ASAYXjKbLTDPhBtYj7X1NuRMDTMtAx9kyBenub1Ev41lo91OHCKdmP+egTDmfQ7Ig=="], "baseline-browser-mapping": ["baseline-browser-mapping@2.11.36", "", { "bin": { "baseline-browser-mapping": "dist/cli.cjs" } }, "sha512-AbPFGS4K9S3meMRCLTM0Zas+e9X7b5ygUBOboDT5zevCZt9ByKSbMtKhwyVqW+oL8WemPVsbP7X3rdZcIyOa0w=="], + "bcryptjs": ["bcryptjs@3.0.3", "", { "bin": { "bcrypt": "bin/bcrypt" } }, "sha512-GlF5wPWnSa/X5LKM1o0wz0suXIINz1iHRLvTS+sLyi7XPbe5ycmYI3DlZqVGZZtDgl4DmasFg7gOB3JYbphV5g=="], + "browserslist": ["browserslist@4.29.5", "", { "dependencies": { "baseline-browser-mapping": "^2.11.33", "caniuse-lite": "^1.0.30001819", "electron-to-chromium": "^1.5.464", "node-releases": "^2.0.60", "update-browserslist-db": "^1.3.4" }, "bin": { "browserslist": "cli.js" } }, "sha512-khTmKu5NGYTotqcYo8Bew2k4RAE9PuXRSFRXG2lQBrCEQHyeQ/vX6gKMxfokwywBsLiXZMnHsmWMG1pnO8J7uQ=="], + "bson": ["bson@7.3.4", "", {}, "sha512-eb33j2Ha6xgygjJ8bhI3sYaI9cLAbSaopZLRJQ5/rxk32XyBSoVVcbeHIzVGJx1SyQpyyoElkoEZgQrg+RVVoA=="], + "caniuse-lite": ["caniuse-lite@1.0.30001826", "", {}, "sha512-wTB+9tIbLro/BCXwLuNV1meHE4L9dS6dArMDwgYfNBPMYScqClHyGunhkCSDtHhe/69Xl0sHlKgBVZ+fNsYtWA=="], "chalk": ["chalk@5.6.2", "", {}, "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA=="], @@ -320,6 +470,8 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cn": ["cn@0.5.2", "", { "bin": { "cn": "bin/cn.mjs" } }, "sha512-wm10E6iuJ37rALOw3D+3cfaXXZSLNMh2uxpQn3dNICqfJM59yXqgbA9/9Iiz155j5kYNLdK6WreNdW3NQtx3OQ=="], + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], "convert-source-map": ["convert-source-map@2.0.0", "", {}, "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="], @@ -340,6 +492,8 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], + "diff": ["diff@9.0.0", "", {}, "sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw=="], "electron-to-chromium": ["electron-to-chromium@1.5.483", "", {}, "sha512-zkGOvf625oaZgK+mhTF4hl3aTAfIOOnR7Jey0e7fUktdJWrB/wsLnzPKvWaqZkMCJ1yzUkAh6IqDJ9GMr3pR2Q=="], @@ -360,6 +514,8 @@ "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], + "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], + "goober": ["goober@2.1.19", "", { "peerDependencies": { "csstype": "^3.0.10" } }, "sha512-U7veizMqxyKlM58+Z5j2ngJBH/r9siDmxpvNxSw0PylF6WQvrASJEZrxh1hidRBJc2jqoBVSyOban5u8m+6Rxg=="], "graceful-fs": ["graceful-fs@4.2.12", "", {}, "sha512-iuyGF2E4oafQfPsRrG2wtY2ILupMhvtdrhzrxB+0VcjsPpSPttpfYdjnGFGsTi5MaJFjI1jOKHPQ6UTbTj9TAw=="], @@ -374,6 +530,8 @@ "jiti": ["jiti@2.7.0", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ=="], + "jose": ["jose@6.2.17", "", {}, "sha512-wOuK9V1yujQFH5S0ZWmLcDZN7+IKh9bUCC2ZPCf2HDlol3WWHy+PPyNjEI75FXRLMp3vDKQcupP59xhVU95hcg=="], + "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], "js-yaml": ["js-yaml@4.3.2", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA=="], @@ -382,6 +540,8 @@ "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], + "kareem": ["kareem@3.4.1", "", {}, "sha512-UlgCAFNAdk/HLSsbfkmbH8pJqpEN52Ek8RG53Ys1i3Q3gbCQruSrWpplZlgIGyDHX/LNoLW97ei3OG43s7UhUA=="], + "launch-editor": ["launch-editor@2.14.2", "", { "dependencies": { "picocolors": "^1.1.1", "shell-quote": "^1.10.0" } }, "sha512-NHJEb30MGBCkmZXLZwz25t37QR4aY03ZzPYUJXOJaOa92uWwExI9SBQUuIznBYc1xkD7cJNGX7oc3Nl3sq2kQQ=="], "lightningcss": ["lightningcss@1.33.0", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-android-arm64": "1.33.0", "lightningcss-darwin-arm64": "1.33.0", "lightningcss-darwin-x64": "1.33.0", "lightningcss-freebsd-x64": "1.33.0", "lightningcss-linux-arm-gnueabihf": "1.33.0", "lightningcss-linux-arm64-gnu": "1.33.0", "lightningcss-linux-arm64-musl": "1.33.0", "lightningcss-linux-x64-gnu": "1.33.0", "lightningcss-linux-x64-musl": "1.33.0", "lightningcss-win32-arm64-msvc": "1.33.0", "lightningcss-win32-x64-msvc": "1.33.0" } }, "sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA=="], @@ -414,6 +574,18 @@ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="], + + "mongodb": ["mongodb@7.8.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.5.0", "bson": "^7.3.2", "mongodb-connection-string-url": "^7.0.2" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.806.0", "@mongodb-js/zstd": "^7.0.0", "gcp-metadata": "^7.0.1", "kerberos": "^7.0.0", "mongodb-client-encryption": "^7.2.0", "snappy": "^7.3.2", "socks": "^2.8.6" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-p2NKagbQ41+daACG/ZSvhTzeAo+DoEyWE9Cmpf/SyP3pTC43+X0mwKE/44PpKtCxp5wOxnXz+t2pXEZRSkt9Yg=="], + + "mongodb-connection-string-url": ["mongodb-connection-string-url@7.0.2", "", { "dependencies": { "@types/whatwg-url": "^13.0.0", "whatwg-url": "^14.1.0" } }, "sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA=="], + + "mongoose": ["mongoose@9.12.1", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "kareem": "3.4.1", "mongodb": "~7.8", "mpath": "0.9.0", "mquery": "6.0.1", "ms": "2.1.3", "sift": "17.1.4" } }, "sha512-wPw1NKurTDShT+kr/JJvhdwPKIJkxFJv5GEbv++QahN8snPR9Fp/BFg6nQbbCyUHBB8DexwwlCQW6SfBD99QCg=="], + + "mpath": ["mpath@0.9.0", "", {}, "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew=="], + + "mquery": ["mquery@6.0.1", "", {}, "sha512-/Nijmq3JcpHwDM+5CwBp5yEl6fUa6jLsIhPsoQywlGXU/ZrKIvSpBzfaGnW9BLqcne/CoUwd1ch7MRqptDgFTA=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], "nanoid": ["nanoid@3.3.20", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ=="], @@ -444,10 +616,20 @@ "prettier": ["prettier@3.10.1", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-70vp3ZUYoPlFbF9uiTdXHUUCGEIp7d4+6p+8DYbkm2jP7x/3i/paXVF6dopSI3zd/CEitrZN1rc4i85xWmmXlg=="], + "punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="], + + "radix-ui": ["radix-ui@1.8.1", "", { "dependencies": { "@radix-ui/primitive": "1.2.0", "@radix-ui/react-accessible-icon": "1.2.0", "@radix-ui/react-accordion": "1.3.1", "@radix-ui/react-alert-dialog": "1.2.1", "@radix-ui/react-arrow": "1.1.17", "@radix-ui/react-aspect-ratio": "1.2.0", "@radix-ui/react-avatar": "1.3.1", "@radix-ui/react-checkbox": "1.4.1", "@radix-ui/react-collapsible": "1.2.1", "@radix-ui/react-collection": "1.2.0", "@radix-ui/react-compose-refs": "1.1.5", "@radix-ui/react-context": "1.3.0", "@radix-ui/react-context-menu": "2.4.1", "@radix-ui/react-dialog": "1.3.1", "@radix-ui/react-direction": "1.2.0", "@radix-ui/react-dismissable-layer": "1.2.1", "@radix-ui/react-dropdown-menu": "2.2.1", "@radix-ui/react-focus-guards": "1.2.0", "@radix-ui/react-focus-scope": "1.3.1", "@radix-ui/react-form": "0.3.0", "@radix-ui/react-hover-card": "1.2.1", "@radix-ui/react-label": "2.1.17", "@radix-ui/react-menu": "2.2.1", "@radix-ui/react-menubar": "1.2.1", "@radix-ui/react-navigation-menu": "1.4.1", "@radix-ui/react-one-time-password-field": "0.2.1", "@radix-ui/react-password-toggle-field": "0.2.1", "@radix-ui/react-popover": "1.3.1", "@radix-ui/react-popper": "1.4.0", "@radix-ui/react-portal": "1.2.0", "@radix-ui/react-presence": "1.1.11", "@radix-ui/react-primitive": "2.2.0", "@radix-ui/react-progress": "1.2.1", "@radix-ui/react-radio-group": "1.5.1", "@radix-ui/react-roving-focus": "1.2.1", "@radix-ui/react-scroll-area": "1.4.0", "@radix-ui/react-select": "2.4.1", "@radix-ui/react-separator": "1.2.0", "@radix-ui/react-slider": "1.6.1", "@radix-ui/react-slot": "1.5.0", "@radix-ui/react-switch": "1.4.1", "@radix-ui/react-tabs": "1.2.1", "@radix-ui/react-toast": "1.3.1", "@radix-ui/react-toggle": "1.2.1", "@radix-ui/react-toggle-group": "1.2.1", "@radix-ui/react-toolbar": "1.2.1", "@radix-ui/react-tooltip": "1.4.1", "@radix-ui/react-use-callback-ref": "1.1.5", "@radix-ui/react-use-controllable-state": "1.3.0", "@radix-ui/react-use-effect-event": "0.0.5", "@radix-ui/react-use-escape-keydown": "1.1.6", "@radix-ui/react-use-is-hydrated": "0.1.3", "@radix-ui/react-use-layout-effect": "1.1.4", "@radix-ui/react-use-size": "1.1.5", "@radix-ui/react-visually-hidden": "1.2.13" }, "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" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-HPfpVGizO9xANlsFSauruLhRe/3+iOt3vPX0MXYle/UQi3gorQrSYtBcdiv0BZYewcl2rvL59qrIuLFMGAmUrw=="], + "react": ["react@19.3.1", "", {}, "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw=="], "react-dom": ["react-dom@19.3.1", "", { "dependencies": { "scheduler": "^0.28.0" }, "peerDependencies": { "react": "^19.3.1" } }, "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ=="], + "react-remove-scroll": ["react-remove-scroll@2.7.2", "", { "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" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q=="], + + "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], + "readdirp": ["readdirp@5.1.1", "", {}, "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA=="], "rolldown": ["rolldown@1.2.19", "", { "dependencies": { "@oxc-project/types": "=0.156.0", "@rolldown/pluginutils": "^1.0.0" }, "optionalDependencies": { "@rolldown/binding-android-arm-eabi": "1.2.19", "@rolldown/binding-android-arm64": "1.2.19", "@rolldown/binding-darwin-arm64": "1.2.19", "@rolldown/binding-darwin-x64": "1.2.19", "@rolldown/binding-freebsd-x64": "1.2.19", "@rolldown/binding-linux-arm-gnueabihf": "1.2.19", "@rolldown/binding-linux-arm64-gnu": "1.2.19", "@rolldown/binding-linux-arm64-musl": "1.2.19", "@rolldown/binding-linux-ppc64-gnu": "1.2.19", "@rolldown/binding-linux-s390x-gnu": "1.2.19", "@rolldown/binding-linux-x64-gnu": "1.2.19", "@rolldown/binding-linux-x64-musl": "1.2.19", "@rolldown/binding-openharmony-arm64": "1.2.19", "@rolldown/binding-wasm32-wasi": "1.2.19", "@rolldown/binding-win32-arm64-msvc": "1.2.19", "@rolldown/binding-win32-x64-msvc": "1.2.19" }, "bin": { "rolldown": "./bin/cli.mjs" } }, "sha512-QIiV1J9d0pzN4jeeTH4sjPy0AZ6cAy2cG0ExeoE1+U8Rvj+hR3gYkokx0YbeUNGlZqiQJJ2utcETe0oKXwzsuA=="], @@ -464,12 +646,16 @@ "shell-quote": ["shell-quote@1.12.0", "", {}, "sha512-PcByqNyT/38F2kDNi006HAMRJaULuBzq/FOsw3qdZvX/GA9W/jamDaRskgHjubHiftXK5sIFxLNkvrXUwcof6Q=="], + "sift": ["sift@17.1.4", "", {}, "sha512-/OIVvj9Ts1mFwTYzW09YODMPeIOnDoigMOvKSIN44OmaCyhuCH/05UPF45XW7Ax0snmzDzCzdlSYqvqsn8+sIg=="], + "solid-js": ["solid-js@1.9.19", "", { "dependencies": { "csstype": "^3.1.0", "seroval": "~1.6.11", "seroval-plugins": "~1.6.8" } }, "sha512-NIrV/6o1FtmrA9iuAAYvnQJZaHrGcaH3LKSVj4kCiZfwqjCtJNDgIgR9XcqYl6KR/Lb3oRx1HrlyswPWR8SRvw=="], "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], "source-map-js": ["source-map-js@1.2.2", "", {}, "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw=="], + "sparse-bitfield": ["sparse-bitfield@3.0.3", "", { "dependencies": { "memory-pager": "^1.0.2" } }, "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="], + "srvx": ["srvx@0.11.22", "", { "bin": { "srvx": "bin/srvx.mjs" } }, "sha512-LqZxxBDMKuMAZzFzJnDCkFOrs9MZQZr0LvHiO/SuSZVdQaXD7xQ5UWTUxheJrQPve1qk9MG2B/yttUvJxw8egQ=="], "tailwind-merge": ["tailwind-merge@3.7.0", "", {}, "sha512-XPPUyAc+cvspz3lHTcR/QgPfW2A0lv/xQNIjX3HGhLR+Nq2lHaLq5MtTesHn8GUr3W3DguT2KT5x3NVgRtYwmA=="], @@ -480,6 +666,8 @@ "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], + "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], + "tslib": ["tslib@2.9.1", "", {}, "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA=="], "tw-animate-css": ["tw-animate-css@1.4.0", "", {}, "sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ=="], @@ -498,6 +686,10 @@ "update-browserslist-db": ["update-browserslist-db@1.3.4", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A=="], + "use-callback-ref": ["use-callback-ref@1.3.3", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg=="], + + "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + "use-sync-external-store": ["use-sync-external-store@1.7.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-6L+EeigHMQhdaIPNIFUKwfWJSwWFQ8gJbJ2DLOs5sDIegTwR9fRxvnM3uciHKjIZhFz+KAv2emhWMRvDmMcY8A=="], "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], @@ -506,8 +698,12 @@ "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], + "webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="], + "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], + "whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="], + "ws": ["ws@8.22.2", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-/rEZ2ejown2N5gxVTHUu9bVVC7ttu0/Tgzf5hbTOixR3Fs7mbbXgn2DbdbcKgJOmpQMtrIUWivtYuVMnp/W/yA=="], "xmlbuilder2": ["xmlbuilder2@4.0.3", "", { "dependencies": { "@oozcitak/dom": "^2.0.2", "@oozcitak/infra": "^2.0.2", "@oozcitak/util": "^10.0.0", "js-yaml": "^4.1.1" } }, "sha512-bx8Q1STctnNaaDymWnkfQLKofs0mGNN7rLLapJlGuV3VlvegD7Ls4ggMjE3aUSWItCCzU0PEv45lI87iSigiCA=="], diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..2cf5004 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2757 @@ +{ + "name": "campusgigs", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "campusgigs", + "dependencies": { + "@tailwindcss/vite": "^4.1.18", + "@tanstack/react-devtools": "0.10.14", + "@tanstack/react-router": "1.170.46", + "@tanstack/react-router-devtools": "1.167.3", + "@tanstack/react-start": "1.169.5", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "lucide-react": "^0.577.0", + "mongoose": "^9.12.1", + "nitro": "3.0.260610-beta", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "tailwind-merge": "^3.0.2", + "tailwindcss": "^4.1.18", + "tw-animate-css": "^1.3.6" + }, + "devDependencies": { + "@biomejs/biome": "2.4.5", + "@tailwindcss/typography": "^0.5.16", + "@tanstack/devtools-vite": "0.8.6", + "@types/node": "^22.10.2", + "@types/react": "^19.2.0", + "@types/react-dom": "^19.2.0", + "@vitejs/plugin-react": "^6.0.1", + "typescript": "^6.0.2", + "vite": "^8.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.13", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.13", + "@babel/helper-compilation-targets": "^7.29.13", + "@babel/helper-module-transforms": "^7.29.13", + "@babel/helpers": "^7.29.13", + "@babel/parser": "^7.29.13", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.13", + "@babel/types": "^7.29.11", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.14", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.14", + "@babel/types": "^7.29.14", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.13", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.14", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.14" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.14", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.14", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.14" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.14", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.14" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.14", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.14", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.14", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.14", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.14", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.14", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@biomejs/biome": { + "version": "2.4.5", + "dev": true, + "license": "MIT OR Apache-2.0", + "bin": { + "biome": "bin/biome" + }, + "engines": { + "node": ">=14.21.3" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/biome" + }, + "optionalDependencies": { + "@biomejs/cli-darwin-arm64": "2.4.5", + "@biomejs/cli-darwin-x64": "2.4.5", + "@biomejs/cli-linux-arm64": "2.4.5", + "@biomejs/cli-linux-arm64-musl": "2.4.5", + "@biomejs/cli-linux-x64": "2.4.5", + "@biomejs/cli-linux-x64-musl": "2.4.5", + "@biomejs/cli-win32-arm64": "2.4.5", + "@biomejs/cli-win32-x64": "2.4.5" + } + }, + "node_modules/@biomejs/cli-linux-x64": { + "version": "2.4.5", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT OR Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=14.21.3" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.6.3.tgz", + "integrity": "sha512-aHGFvT95c82CoQ7hb6qXpz8Hcc5PJb3ZtQ+P01QDBbyQday0mjcrJPeNJOHCgWe0pGX0zKmKP+1mWi/I/BCX9Q==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@neodrag/core": { + "version": "3.0.0-next.11", + "license": "MIT" + }, + "node_modules/@neodrag/solid": { + "version": "3.0.0-next.11", + "license": "MIT", + "peerDependencies": { + "@neodrag/core": "3.0.0-next.11", + "solid-js": "^1.0.0" + } + }, + "node_modules/@oozcitak/dom": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "^2.0.2", + "@oozcitak/url": "^3.0.0", + "@oozcitak/util": "^10.0.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oozcitak/infra": { + "version": "2.0.2", + "license": "MIT", + "dependencies": { + "@oozcitak/util": "^10.0.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oozcitak/url": { + "version": "3.0.0", + "license": "MIT", + "dependencies": { + "@oozcitak/infra": "^2.0.2", + "@oozcitak/util": "^10.0.0" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oozcitak/util": { + "version": "10.0.0", + "license": "MIT", + "engines": { + "node": ">=20.0" + } + }, + "node_modules/@oxc-parser/binding-linux-x64-gnu": { + "version": "0.120.0", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.156.0", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/oxc-project" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.2.19", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.1.0", + "license": "MIT" + }, + "node_modules/@solid-primitives/event-listener": { + "version": "2.4.6", + "license": "MIT", + "dependencies": { + "@solid-primitives/utils": "^6.4.1" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/keyboard": { + "version": "1.3.7", + "license": "MIT", + "dependencies": { + "@solid-primitives/event-listener": "^2.4.6", + "@solid-primitives/rootless": "^1.5.4", + "@solid-primitives/utils": "^6.4.1" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/resize-observer": { + "version": "2.2.0", + "license": "MIT", + "dependencies": { + "@solid-primitives/event-listener": "^2.4.6", + "@solid-primitives/rootless": "^1.5.4", + "@solid-primitives/static-store": "^0.1.4", + "@solid-primitives/utils": "^6.4.1" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/rootless": { + "version": "1.5.4", + "license": "MIT", + "dependencies": { + "@solid-primitives/utils": "^6.4.1" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/static-store": { + "version": "0.1.4", + "license": "MIT", + "dependencies": { + "@solid-primitives/utils": "^6.4.1" + }, + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@solid-primitives/utils": { + "version": "6.4.1", + "license": "MIT", + "peerDependencies": { + "solid-js": "^1.6.12" + } + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.5", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.5", + "jiti": "^2.7.0", + "lightningcss": "1.33.0", + "magic-string": "^1.1.0", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.5" + } + }, + "node_modules/@tailwindcss/node/node_modules/magic-string": { + "version": "1.4.5", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.6.0" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.5", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.5", + "@tailwindcss/oxide-darwin-arm64": "4.3.5", + "@tailwindcss/oxide-darwin-x64": "4.3.5", + "@tailwindcss/oxide-freebsd-x64": "4.3.5", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.5", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.5", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.5", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.5", + "@tailwindcss/oxide-linux-x64-musl": "4.3.5", + "@tailwindcss/oxide-wasm32-wasi": "4.3.5", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.5", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.5" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.5", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.5", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.3", + "@emnapi/runtime": "^1.11.3", + "@emnapi/wasi-threads": "^1.2.3", + "@napi-rs/wasm-runtime": "^1.2.2", + "@tybys/wasm-util": "^0.10.3", + "tslib": "^2.8.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/core": { + "version": "1.11.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.3", + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/runtime": { + "version": "1.11.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@emnapi/wasi-threads": { + "version": "1.2.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@napi-rs/wasm-runtime": { + "version": "1.2.2", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.3" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=23.5.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1 || ^2.0.0-alpha.3", + "@emnapi/runtime": "^1.7.1 || ^2.0.0-alpha.3" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/@tybys/wasm-util": { + "version": "0.10.3", + "inBundle": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi/node_modules/tslib": { + "version": "2.8.1", + "inBundle": true, + "license": "0BSD", + "optional": true + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.20", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || >=4.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.5", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.5", + "@tailwindcss/oxide": "4.3.5", + "tailwindcss": "4.3.5" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@tanstack/devtools": { + "version": "0.16.0", + "license": "MIT", + "dependencies": { + "@neodrag/core": "3.0.0-next.11", + "@neodrag/solid": "3.0.0-next.11", + "@solid-primitives/event-listener": "^2.4.3", + "@solid-primitives/keyboard": "^1.3.3", + "@solid-primitives/resize-observer": "^2.1.3", + "@tanstack/devtools-client": "0.0.8", + "@tanstack/devtools-event-bus": "0.4.3", + "@tanstack/devtools-ui": "0.7.2", + "clsx": "^2.1.1", + "goober": "^2.1.16", + "solid-js": "^1.9.13" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "solid-js": "^1.9.13" + } + }, + "node_modules/@tanstack/devtools-bundler-core": { + "version": "0.1.4", + "dev": true, + "license": "MIT", + "dependencies": { + "@tanstack/devtools-client": "0.0.8", + "@tanstack/devtools-event-bus": "0.4.3", + "chalk": "^5.6.2", + "launch-editor": "^2.14.1", + "magic-string": "^0.30.0", + "oxc-parser": "^0.120.0", + "picomatch": "^4.0.5" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/devtools-client": { + "version": "0.0.8", + "license": "MIT", + "dependencies": { + "@tanstack/devtools-event-client": "^0.5.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/devtools-event-bus": { + "version": "0.4.3", + "license": "MIT", + "dependencies": { + "ws": "^8.18.3" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/devtools-event-client": { + "version": "0.5.1", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/devtools-ui": { + "version": "0.7.2", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1", + "dayjs": "^1.11.19", + "goober": "^2.1.16", + "solid-js": "^1.9.13" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "solid-js": "^1.9.13" + } + }, + "node_modules/@tanstack/devtools-vite": { + "version": "0.8.6", + "dev": true, + "license": "MIT", + "dependencies": { + "@tanstack/devtools-bundler-core": "0.1.4", + "@tanstack/devtools-client": "0.0.8", + "@tanstack/devtools-event-bus": "0.4.3", + "chalk": "^5.6.2" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@tanstack/history": { + "version": "1.162.5", + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-devtools": { + "version": "0.10.14", + "license": "MIT", + "dependencies": { + "@tanstack/devtools": "0.16.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@types/react": ">=16.8", + "@types/react-dom": ">=16.8", + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/@tanstack/react-router": { + "version": "1.170.46", + "license": "MIT", + "dependencies": { + "@tanstack/history": "1.162.5", + "@tanstack/react-store": "^0.11.2", + "@tanstack/router-core": "1.171.39", + "isbot": "^5.1.22" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0" + } + }, + "node_modules/@tanstack/react-router-devtools": { + "version": "1.167.3", + "license": "MIT", + "dependencies": { + "@tanstack/router-devtools-core": "1.168.3" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-router": "^1.170.46", + "@tanstack/router-core": "^1.171.39", + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0" + }, + "peerDependenciesMeta": { + "@tanstack/router-core": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-start": { + "version": "1.169.5", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.46", + "@tanstack/react-start-client": "1.168.45", + "@tanstack/react-start-rsc": "0.1.66", + "@tanstack/react-start-server": "1.167.52", + "@tanstack/router-utils": "1.163.0", + "@tanstack/start-client-core": "1.170.39", + "@tanstack/start-plugin-core": "1.171.56", + "@tanstack/start-server-core": "1.169.45", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rsbuild/core": "^2.0.5", + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0", + "vite": ">=8.0.0" + }, + "peerDependenciesMeta": { + "@rsbuild/core": { + "optional": true + }, + "@vitejs/plugin-rsc": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-start-client": { + "version": "1.168.45", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.46", + "@tanstack/router-core": "1.171.39", + "@tanstack/start-client-core": "1.170.39" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0" + } + }, + "node_modules/@tanstack/react-start-rsc": { + "version": "0.1.66", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.46", + "@tanstack/router-core": "1.171.39", + "@tanstack/router-utils": "1.163.0", + "@tanstack/start-client-core": "1.170.39", + "@tanstack/start-fn-stubs": "1.162.0", + "@tanstack/start-plugin-core": "1.171.56", + "@tanstack/start-storage-context": "1.167.41", + "pathe": "^2.0.3" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rspack/core": ">=2.0.0-0", + "@vitejs/plugin-rsc": ">=0.5.30", + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0", + "react-server-dom-rspack": ">=0.0.2" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "@vitejs/plugin-rsc": { + "optional": true + }, + "react-server-dom-rspack": { + "optional": true + } + } + }, + "node_modules/@tanstack/react-start-server": { + "version": "1.167.52", + "license": "MIT", + "dependencies": { + "@tanstack/react-router": "1.170.46", + "@tanstack/router-core": "1.171.39", + "@tanstack/start-server-core": "1.169.45" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": ">=18.0.0 || >=19.0.0", + "react-dom": ">=18.0.0 || >=19.0.0" + } + }, + "node_modules/@tanstack/react-store": { + "version": "0.11.2", + "license": "MIT", + "dependencies": { + "@tanstack/store": "0.11.2", + "use-sync-external-store": "^1.6.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tanstack/router-core": { + "version": "1.171.39", + "license": "MIT", + "dependencies": { + "@tanstack/history": "1.162.5", + "cookie-es": "^3.0.0", + "seroval": "^1.6.7", + "seroval-plugins": "^1.6.7" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/router-devtools-core": { + "version": "1.168.3", + "license": "MIT", + "dependencies": { + "clsx": "^2.1.1", + "goober": "^2.1.16" + }, + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/router-core": "^1.171.39", + "csstype": "^3.0.10" + }, + "peerDependenciesMeta": { + "csstype": { + "optional": true + } + } + }, + "node_modules/@tanstack/router-generator": { + "version": "1.168.0", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.8", + "@tanstack/router-core": "1.171.39", + "@tanstack/router-utils": "1.163.0", + "@tanstack/virtual-file-routes": "1.162.0", + "jiti": "^2.7.0", + "magic-string": "^0.30.21", + "prettier": "^3.9.6", + "zod": "^4.5.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/router-plugin": { + "version": "1.169.0", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "@tanstack/router-core": "1.171.39", + "@tanstack/router-generator": "1.168.0", + "@tanstack/router-utils": "1.163.0", + "chokidar": "^5.0.0", + "unplugin": "^3.5.0", + "zod": "^4.5.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rsbuild/core": ">=1.0.2 || ^2.0.0", + "@tanstack/react-router": "^1.170.46", + "vite": ">=6.0.0", + "vite-plugin-solid": "^2.11.10 || ^3.0.0-0", + "webpack": ">=5.92.0" + }, + "peerDependenciesMeta": { + "@rsbuild/core": { + "optional": true + }, + "@tanstack/react-router": { + "optional": true + }, + "vite": { + "optional": true + }, + "vite-plugin-solid": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/@tanstack/router-utils": { + "version": "1.163.0", + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.29.8", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "ansis": "^4.3.1", + "babel-dead-code-elimination": "^1.0.12", + "diff": "^9.0.0", + "pathe": "^2.0.3", + "tinyglobby": "^0.2.17" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-client-core": { + "version": "1.170.39", + "license": "MIT", + "dependencies": { + "@tanstack/router-core": "1.171.39", + "@tanstack/start-fn-stubs": "1.162.0", + "@tanstack/start-storage-context": "1.167.41", + "seroval": "^1.6.7" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-fn-stubs": { + "version": "1.162.0", + "license": "MIT", + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-plugin-core": { + "version": "1.171.56", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "7.29.7", + "@babel/core": "^7.29.7", + "@babel/types": "^7.29.8", + "@jridgewell/remapping": "^2.3.5", + "@tanstack/router-core": "1.171.39", + "@tanstack/router-generator": "1.168.0", + "@tanstack/router-plugin": "1.169.0", + "@tanstack/router-utils": "1.163.0", + "@tanstack/start-server-core": "1.169.45", + "exsolve": "^1.1.3", + "lightningcss": "^1.33.0", + "pathe": "^2.0.3", + "picomatch": "^4.0.7", + "seroval": "^1.6.7", + "source-map": "^0.7.6", + "srvx": "^1.0.0", + "tinyglobby": "^0.2.17", + "ufo": "^1.6.4", + "vitefu": "^1.1.3", + "xmlbuilder2": "^4.0.3", + "zod": "^4.5.4" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@rsbuild/core": "^2.0.5", + "vite": ">=8.0.0" + }, + "peerDependenciesMeta": { + "@rsbuild/core": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@tanstack/start-plugin-core/node_modules/srvx": { + "version": "1.0.8", + "license": "MIT", + "bin": { + "srvx": "bin/srvx.mjs" + }, + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/@tanstack/start-server-core": { + "version": "1.169.45", + "license": "MIT", + "dependencies": { + "@tanstack/history": "1.162.5", + "@tanstack/router-core": "1.171.39", + "@tanstack/start-client-core": "1.170.39", + "@tanstack/start-storage-context": "1.167.41", + "cookie-es": "^3.0.0", + "fetchdts": "^0.2.0", + "seroval": "^1.6.7" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/start-storage-context": { + "version": "1.167.41", + "license": "MIT", + "dependencies": { + "@tanstack/router-core": "1.171.39" + }, + "engines": { + "node": ">=22.12.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/store": { + "version": "0.11.2", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/virtual-file-routes": { + "version": "1.162.0", + "license": "MIT", + "engines": { + "node": ">=20.19" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@types/node": { + "version": "22.20.5", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.3.0", + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.3.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.1" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0 || ^0.3.0", + "babel-plugin-react-compiler": "^1.0.0", + "oxc-transform-react": ">=0.152.0 <0.157.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "oxc-transform-react": { + "optional": true + } + } + }, + "node_modules/ansis": { + "version": "4.5.0", + "license": "ISC", + "engines": { + "node": ">=14" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "license": "Python-2.0" + }, + "node_modules/babel-dead-code-elimination": { + "version": "1.0.12", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.7", + "@babel/parser": "^7.23.6", + "@babel/traverse": "^7.23.7", + "@babel/types": "^7.23.6" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.36", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.29.5", + "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.11.33", + "caniuse-lite": "^1.0.30001819", + "electron-to-chromium": "^1.5.464", + "node-releases": "^2.0.60", + "update-browserslist-db": "^1.3.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/bson": { + "version": "7.3.4", + "resolved": "https://registry.npmjs.org/bson/-/bson-7.3.4.tgz", + "integrity": "sha512-eb33j2Ha6xgygjJ8bhI3sYaI9cLAbSaopZLRJQ5/rxk32XyBSoVVcbeHIzVGJx1SyQpyyoElkoEZgQrg+RVVoA==", + "license": "Apache-2.0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001826", + "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/chalk": { + "version": "5.6.2", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chokidar": { + "version": "5.0.0", + "license": "MIT", + "dependencies": { + "readdirp": "^5.0.0" + }, + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/class-variance-authority": { + "version": "0.7.1", + "license": "Apache-2.0", + "dependencies": { + "clsx": "^2.1.1" + }, + "funding": { + "url": "https://polar.sh/cva" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/consola": { + "version": "3.4.2", + "license": "MIT", + "engines": { + "node": "^14.18.0 || >=16.10.0" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "license": "MIT" + }, + "node_modules/cookie-es": { + "version": "3.1.1", + "license": "MIT" + }, + "node_modules/crossws": { + "version": "0.4.17", + "license": "MIT", + "peerDependencies": { + "srvx": ">=0.11.5" + }, + "peerDependenciesMeta": { + "srvx": { + "optional": true + } + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "license": "MIT" + }, + "node_modules/dayjs": { + "version": "1.11.23", + "license": "MIT" + }, + "node_modules/db0": { + "version": "0.3.4", + "license": "MIT", + "peerDependencies": { + "@electric-sql/pglite": "*", + "@libsql/client": "*", + "better-sqlite3": "*", + "drizzle-orm": "*", + "mysql2": "*", + "sqlite3": "*" + }, + "peerDependenciesMeta": { + "@electric-sql/pglite": { + "optional": true + }, + "@libsql/client": { + "optional": true + }, + "better-sqlite3": { + "optional": true + }, + "drizzle-orm": { + "optional": true + }, + "mysql2": { + "optional": true + }, + "sqlite3": { + "optional": true + } + } + }, + "node_modules/debug": { + "version": "4.4.3", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/diff": { + "version": "9.0.0", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.483", + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.2", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/env-runner": { + "version": "0.1.16", + "license": "MIT", + "dependencies": { + "crossws": "^0.4.8", + "exsolve": "^1.1.0", + "httpxy": "^0.5.4", + "srvx": "^0.11.19" + }, + "bin": { + "env-runner": "dist/cli.mjs" + }, + "peerDependencies": { + "@netlify/runtime": "^4.1.23", + "@vercel/queue": ">=0.2.0", + "miniflare": "^4.20260515.0", + "wrangler": "^4.0.0" + }, + "peerDependenciesMeta": { + "@netlify/runtime": { + "optional": true + }, + "@vercel/queue": { + "optional": true + }, + "miniflare": { + "optional": true + }, + "wrangler": { + "optional": true + } + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/exsolve": { + "version": "1.2.0", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fetchdts": { + "version": "0.2.0", + "license": "MIT" + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/goober": { + "version": "2.1.19", + "license": "MIT", + "peerDependencies": { + "csstype": "^3.0.10" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.12", + "license": "BlueOak-1.0.0" + }, + "node_modules/h3": { + "version": "2.0.1-rc.22", + "license": "MIT", + "dependencies": { + "rou3": "^0.8.1", + "srvx": "^0.11.15" + }, + "bin": { + "h3": "bin/h3.mjs" + }, + "engines": { + "node": ">=20.11.1" + }, + "peerDependencies": { + "crossws": "^0.4.1" + }, + "peerDependenciesMeta": { + "crossws": { + "optional": true + } + } + }, + "node_modules/hookable": { + "version": "6.1.2", + "license": "MIT" + }, + "node_modules/httpxy": { + "version": "0.5.6", + "license": "MIT" + }, + "node_modules/isbot": { + "version": "5.2.3", + "license": "Unlicense", + "engines": { + "node": ">=18" + } + }, + "node_modules/jiti": { + "version": "2.7.0", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/kareem": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/kareem/-/kareem-3.4.1.tgz", + "integrity": "sha512-UlgCAFNAdk/HLSsbfkmbH8pJqpEN52Ek8RG53Ys1i3Q3gbCQruSrWpplZlgIGyDHX/LNoLW97ei3OG43s7UhUA==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/launch-editor": { + "version": "2.14.2", + "dev": true, + "license": "MIT", + "dependencies": { + "picocolors": "^1.1.1", + "shell-quote": "^1.10.0" + } + }, + "node_modules/lightningcss": { + "version": "1.33.0", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.33.0", + "lightningcss-darwin-arm64": "1.33.0", + "lightningcss-darwin-x64": "1.33.0", + "lightningcss-freebsd-x64": "1.33.0", + "lightningcss-linux-arm-gnueabihf": "1.33.0", + "lightningcss-linux-arm64-gnu": "1.33.0", + "lightningcss-linux-arm64-musl": "1.33.0", + "lightningcss-linux-x64-gnu": "1.33.0", + "lightningcss-linux-x64-musl": "1.33.0", + "lightningcss-win32-arm64-msvc": "1.33.0", + "lightningcss-win32-x64-msvc": "1.33.0" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.33.0", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "license": "BlueOak-1.0.0", + "optional": true, + "peer": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lucide-react": { + "version": "0.577.0", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, + "node_modules/mongodb": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-7.8.0.tgz", + "integrity": "sha512-p2NKagbQ41+daACG/ZSvhTzeAo+DoEyWE9Cmpf/SyP3pTC43+X0mwKE/44PpKtCxp5wOxnXz+t2pXEZRSkt9Yg==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.5.0", + "bson": "^7.3.2", + "mongodb-connection-string-url": "^7.0.2" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.806.0", + "@mongodb-js/zstd": "^7.0.0", + "gcp-metadata": "^7.0.1", + "kerberos": "^7.0.0", + "mongodb-client-encryption": "^7.2.0", + "snappy": "^7.3.2", + "socks": "^2.8.6" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-7.0.2.tgz", + "integrity": "sha512-ZoS07RoFqpKYQwAk59qmrx8+jJHNHU30UjlU96QktiGn1ltvDr+vCznLX5DiUBLEpMAHatHNWV1nM/74ul66kA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^13.0.0", + "whatwg-url": "^14.1.0" + }, + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/mongoose": { + "version": "9.12.1", + "resolved": "https://registry.npmjs.org/mongoose/-/mongoose-9.12.1.tgz", + "integrity": "sha512-wPw1NKurTDShT+kr/JJvhdwPKIJkxFJv5GEbv++QahN8snPR9Fp/BFg6nQbbCyUHBB8DexwwlCQW6SfBD99QCg==", + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "kareem": "3.4.1", + "mongodb": "~7.8", + "mpath": "0.9.0", + "mquery": "6.0.1", + "ms": "2.1.3", + "sift": "17.1.4" + }, + "engines": { + "node": ">=20.19.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mongoose" + } + }, + "node_modules/mpath": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/mpath/-/mpath-0.9.0.tgz", + "integrity": "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mquery": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/mquery/-/mquery-6.0.1.tgz", + "integrity": "sha512-/Nijmq3JcpHwDM+5CwBp5yEl6fUa6jLsIhPsoQywlGXU/ZrKIvSpBzfaGnW9BLqcne/CoUwd1ch7MRqptDgFTA==", + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "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/nf3": { + "version": "0.3.24", + "license": "MIT" + }, + "node_modules/nitro": { + "version": "3.0.260610-beta", + "license": "MIT", + "dependencies": { + "consola": "^3.4.2", + "crossws": "^0.4.6", + "db0": "^0.3.4", + "env-runner": "^0.1.12", + "h3": "2.0.1-rc.22", + "hookable": "^6.1.1", + "nf3": "^0.3.17", + "ocache": "^0.1.5", + "ofetch": "2.0.0-alpha.3", + "ohash": "^2.0.11", + "rolldown": "^1.1.0", + "srvx": "^0.11.16", + "unenv": "2.0.0-rc.24", + "unstorage": "2.0.0-alpha.7" + }, + "bin": { + "nitro": "dist/cli/index.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@vercel/queue": "^0.3.0", + "dotenv": "*", + "giget": "*", + "jiti": "^2.7.0", + "rollup": "^4.61.1", + "vite": "^7 || ^8", + "xml2js": "^0.6.2", + "zephyr-agent": "^0.2.0" + }, + "peerDependenciesMeta": { + "@vercel/queue": { + "optional": true + }, + "dotenv": { + "optional": true + }, + "giget": { + "optional": true + }, + "jiti": { + "optional": true + }, + "rollup": { + "optional": true + }, + "vite": { + "optional": true + }, + "xml2js": { + "optional": true + }, + "zephyr-agent": { + "optional": true + } + } + }, + "node_modules/node-releases": { + "version": "2.0.63", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/ocache": { + "version": "0.1.5", + "license": "MIT", + "dependencies": { + "ohash": "^2.0.11" + } + }, + "node_modules/ofetch": { + "version": "2.0.0-alpha.3", + "license": "MIT" + }, + "node_modules/ohash": { + "version": "2.0.12", + "license": "MIT" + }, + "node_modules/oxc-parser": { + "version": "0.120.0", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "^0.120.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/sponsors/Boshen" + }, + "optionalDependencies": { + "@oxc-parser/binding-android-arm-eabi": "0.120.0", + "@oxc-parser/binding-android-arm64": "0.120.0", + "@oxc-parser/binding-darwin-arm64": "0.120.0", + "@oxc-parser/binding-darwin-x64": "0.120.0", + "@oxc-parser/binding-freebsd-x64": "0.120.0", + "@oxc-parser/binding-linux-arm-gnueabihf": "0.120.0", + "@oxc-parser/binding-linux-arm-musleabihf": "0.120.0", + "@oxc-parser/binding-linux-arm64-gnu": "0.120.0", + "@oxc-parser/binding-linux-arm64-musl": "0.120.0", + "@oxc-parser/binding-linux-ppc64-gnu": "0.120.0", + "@oxc-parser/binding-linux-riscv64-gnu": "0.120.0", + "@oxc-parser/binding-linux-riscv64-musl": "0.120.0", + "@oxc-parser/binding-linux-s390x-gnu": "0.120.0", + "@oxc-parser/binding-linux-x64-gnu": "0.120.0", + "@oxc-parser/binding-linux-x64-musl": "0.120.0", + "@oxc-parser/binding-openharmony-arm64": "0.120.0", + "@oxc-parser/binding-wasm32-wasi": "0.120.0", + "@oxc-parser/binding-win32-arm64-msvc": "0.120.0", + "@oxc-parser/binding-win32-ia32-msvc": "0.120.0", + "@oxc-parser/binding-win32-x64-msvc": "0.120.0" + } + }, + "node_modules/oxc-parser/node_modules/@oxc-project/types": { + "version": "0.120.0", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.32", + "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.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prettier": { + "version": "3.10.1", + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/react": { + "version": "19.3.1", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/readdirp": { + "version": "5.1.1", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/rolldown": { + "version": "1.2.19", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.156.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm-eabi": "1.2.19", + "@rolldown/binding-android-arm64": "1.2.19", + "@rolldown/binding-darwin-arm64": "1.2.19", + "@rolldown/binding-darwin-x64": "1.2.19", + "@rolldown/binding-freebsd-x64": "1.2.19", + "@rolldown/binding-linux-arm-gnueabihf": "1.2.19", + "@rolldown/binding-linux-arm64-gnu": "1.2.19", + "@rolldown/binding-linux-arm64-musl": "1.2.19", + "@rolldown/binding-linux-ppc64-gnu": "1.2.19", + "@rolldown/binding-linux-s390x-gnu": "1.2.19", + "@rolldown/binding-linux-x64-gnu": "1.2.19", + "@rolldown/binding-linux-x64-musl": "1.2.19", + "@rolldown/binding-openharmony-arm64": "1.2.19", + "@rolldown/binding-wasm32-wasi": "1.2.19", + "@rolldown/binding-win32-arm64-msvc": "1.2.19", + "@rolldown/binding-win32-x64-msvc": "1.2.19" + } + }, + "node_modules/rou3": { + "version": "0.8.1", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.28.0", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.2", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/seroval": { + "version": "1.6.12", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.6.12", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/shell-quote": { + "version": "1.12.0", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/sift": { + "version": "17.1.4", + "resolved": "https://registry.npmjs.org/sift/-/sift-17.1.4.tgz", + "integrity": "sha512-/OIVvj9Ts1mFwTYzW09YODMPeIOnDoigMOvKSIN44OmaCyhuCH/05UPF45XW7Ax0snmzDzCzdlSYqvqsn8+sIg==", + "license": "MIT" + }, + "node_modules/solid-js": { + "version": "1.9.19", + "license": "MIT", + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.6.11", + "seroval-plugins": "~1.6.8" + } + }, + "node_modules/source-map": { + "version": "0.7.6", + "license": "BSD-3-Clause", + "engines": { + "node": ">= 12" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/srvx": { + "version": "0.11.22", + "license": "MIT", + "bin": { + "srvx": "bin/srvx.mjs" + }, + "engines": { + "node": ">=20.16.0" + } + }, + "node_modules/tailwind-merge": { + "version": "3.7.0", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.5", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/tw-animate-css": { + "version": "1.4.0", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Wombosvideo" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/ufo": { + "version": "1.6.4", + "license": "MIT" + }, + "node_modules/undici-types": { + "version": "6.21.0", + "devOptional": true, + "license": "MIT" + }, + "node_modules/unenv": { + "version": "2.0.0-rc.24", + "license": "MIT", + "dependencies": { + "pathe": "^2.0.3" + } + }, + "node_modules/unplugin": { + "version": "3.7.0", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "@rolldown/pluginutils": "^1.0.1", + "picomatch": "^4.0.7", + "webpack-virtual-modules": "^0.6.2" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@farmfe/core": "*", + "@rsbuild/core": "*", + "@rspack/core": "*", + "bun-types-no-globals": "*", + "esbuild": "*", + "rolldown": "*", + "rollup": "*", + "unloader": "*", + "vite": "*", + "webpack": "*" + }, + "peerDependenciesMeta": { + "@farmfe/core": { + "optional": true + }, + "@rsbuild/core": { + "optional": true + }, + "@rspack/core": { + "optional": true + }, + "bun-types-no-globals": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "rolldown": { + "optional": true + }, + "rollup": { + "optional": true + }, + "unloader": { + "optional": true + }, + "vite": { + "optional": true + }, + "webpack": { + "optional": true + } + } + }, + "node_modules/unstorage": { + "version": "2.0.0-alpha.7", + "license": "MIT", + "peerDependencies": { + "@azure/app-configuration": "^1.11.0", + "@azure/cosmos": "^4.9.1", + "@azure/data-tables": "^13.3.2", + "@azure/identity": "^4.13.0", + "@azure/keyvault-secrets": "^4.10.0", + "@azure/storage-blob": "^12.31.0", + "@capacitor/preferences": "^6 || ^7 || ^8", + "@deno/kv": ">=0.13.0", + "@netlify/blobs": "^6.5.0 || ^7.0.0 || ^8.1.0 || ^9.0.0 || ^10.0.0", + "@planetscale/database": "^1.19.0", + "@upstash/redis": "^1.36.2", + "@vercel/blob": ">=0.27.3", + "@vercel/functions": "^2.2.12 || ^3.0.0", + "@vercel/kv": "^1.0.1", + "aws4fetch": "^1.0.20", + "chokidar": "^4 || ^5", + "db0": ">=0.3.4", + "idb-keyval": "^6.2.2", + "ioredis": "^5.9.3", + "lru-cache": "^11.2.6", + "mongodb": "^6 || ^7", + "ofetch": "*", + "uploadthing": "^7.7.4" + }, + "peerDependenciesMeta": { + "@azure/app-configuration": { + "optional": true + }, + "@azure/cosmos": { + "optional": true + }, + "@azure/data-tables": { + "optional": true + }, + "@azure/identity": { + "optional": true + }, + "@azure/keyvault-secrets": { + "optional": true + }, + "@azure/storage-blob": { + "optional": true + }, + "@capacitor/preferences": { + "optional": true + }, + "@deno/kv": { + "optional": true + }, + "@netlify/blobs": { + "optional": true + }, + "@planetscale/database": { + "optional": true + }, + "@upstash/redis": { + "optional": true + }, + "@vercel/blob": { + "optional": true + }, + "@vercel/functions": { + "optional": true + }, + "@vercel/kv": { + "optional": true + }, + "aws4fetch": { + "optional": true + }, + "chokidar": { + "optional": true + }, + "db0": { + "optional": true + }, + "idb-keyval": { + "optional": true + }, + "ioredis": { + "optional": true + }, + "lru-cache": { + "optional": true + }, + "mongodb": { + "optional": true + }, + "ofetch": { + "optional": true + }, + "uploadthing": { + "optional": true + } + } + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "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-sync-external-store": { + "version": "1.7.0", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.3.8", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.33.0", + "picomatch": "^4.0.7", + "postcss": "^8.5.32", + "rolldown": "~1.2.18", + "tinyglobby": "^0.2.17" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.7.1 || ^0.8.0", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "license": "MIT" + }, + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", + "license": "MIT", + "dependencies": { + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/ws": { + "version": "8.22.2", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlbuilder2": { + "version": "4.0.3", + "license": "MIT", + "dependencies": { + "@oozcitak/dom": "^2.0.2", + "@oozcitak/infra": "^2.0.2", + "@oozcitak/util": "^10.0.0", + "js-yaml": "^4.1.1" + }, + "engines": { + "node": ">=20.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + }, + "node_modules/zod": { + "version": "4.6.7", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/package.json b/package.json index 402ae0a..bb92085 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ "generate-routes": "vite build", "build": "vite build", "preview": "vite preview", + "test:db": "node --experimental-strip-types --env-file=.env scripts/test-db.ts", "format": "biome format", "lint": "biome lint", "check": "biome check" @@ -20,10 +21,15 @@ "@tanstack/react-router": "1.170.46", "@tanstack/react-router-devtools": "1.167.3", "@tanstack/react-start": "1.169.5", + "bcryptjs": "^3.0.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cn": "^0.5.2", + "jose": "^6.2.17", "lucide-react": "^0.577.0", + "mongoose": "^9.12.1", "nitro": "3.0.260610-beta", + "radix-ui": "^1.8.1", "react": "^19.2.0", "react-dom": "^19.2.0", "tailwind-merge": "^3.0.2", @@ -34,6 +40,7 @@ "@biomejs/biome": "2.4.5", "@tailwindcss/typography": "^0.5.16", "@tanstack/devtools-vite": "0.8.6", + "@types/bcryptjs": "^2.4.6", "@types/node": "^22.10.2", "@types/react": "^19.2.0", "@types/react-dom": "^19.2.0", @@ -47,4 +54,4 @@ "lightningcss" ] } -} \ No newline at end of file +} diff --git a/scripts/test-db.ts b/scripts/test-db.ts new file mode 100644 index 0000000..ba357ad --- /dev/null +++ b/scripts/test-db.ts @@ -0,0 +1,13 @@ +import { connectDB } from "../src/lib/db.ts"; + +async function main() { + try { + await connectDB(); + process.exit(0); + } catch (error) { + console.error("Connection failed:", error); + process.exit(1); + } +} + +main(); diff --git a/src/components/ui/badge.tsx b/src/components/ui/badge.tsx new file mode 100644 index 0000000..444c730 --- /dev/null +++ b/src/components/ui/badge.tsx @@ -0,0 +1,47 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "cn"; +import { Slot } from "radix-ui"; +import type * as React from "react"; + +const badgeVariants = cva( + "inline-flex w-fit shrink-0 items-center justify-center gap-1 overflow-hidden rounded-full border border-transparent px-2 py-0.5 text-xs font-medium whitespace-nowrap transition-[color,box-shadow] focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&>svg]:pointer-events-none [&>svg]:size-3", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground [a&]:hover:bg-primary/90", + secondary: + "bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90", + destructive: + "bg-destructive text-white focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40 [a&]:hover:bg-destructive/90", + outline: + "border-border text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + ghost: "[a&]:hover:bg-accent [a&]:hover:text-accent-foreground", + link: "text-primary underline-offset-4 [a&]:hover:underline", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +function Badge({ + className, + variant = "default", + asChild = false, + ...props +}: React.ComponentProps<"span"> & + VariantProps & { asChild?: boolean }) { + const Comp = asChild ? Slot.Root : "span"; + + return ( + + ); +} + +export { Badge, badgeVariants }; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 0000000..6b7c71d --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,63 @@ +import { cva, type VariantProps } from "class-variance-authority"; +import { cn } from "cn"; +import { Slot } from "radix-ui"; +import type * as React from "react"; + +const buttonVariants = cva( + "inline-flex shrink-0 items-center justify-center gap-2 rounded-md text-sm font-medium whitespace-nowrap transition-all outline-none focus-visible:border-ring focus-visible:ring-[3px] focus-visible:ring-ring/50 disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:bg-destructive/60 dark:focus-visible:ring-destructive/40", + outline: + "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: + "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2 has-[>svg]:px-3", + xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3", + sm: "h-8 gap-1.5 rounded-md px-3 has-[>svg]:px-2.5", + lg: "h-10 rounded-md px-6 has-[>svg]:px-4", + icon: "size-9", + "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3", + "icon-sm": "size-8", + "icon-lg": "size-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + }, +); + +function Button({ + className, + variant = "default", + size = "default", + asChild = false, + ...props +}: React.ComponentProps<"button"> & + VariantProps & { + asChild?: boolean; + }) { + const Comp = asChild ? Slot.Root : "button"; + + return ( + + ); +} + +export { Button, buttonVariants }; diff --git a/src/components/ui/card.tsx b/src/components/ui/card.tsx new file mode 100644 index 0000000..95298f3 --- /dev/null +++ b/src/components/ui/card.tsx @@ -0,0 +1,91 @@ +import { cn } from "cn"; +import type * as React from "react"; + +function Card({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardHeader({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardTitle({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardDescription({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardAction({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardContent({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +function CardFooter({ className, ...props }: React.ComponentProps<"div">) { + return ( +
+ ); +} + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardAction, + CardDescription, + CardContent, +}; diff --git a/src/components/ui/input.tsx b/src/components/ui/input.tsx new file mode 100644 index 0000000..1fe736e --- /dev/null +++ b/src/components/ui/input.tsx @@ -0,0 +1,20 @@ +import { cn } from "cn"; +import type * as React from "react"; + +function Input({ className, type, ...props }: React.ComponentProps<"input">) { + return ( + + ); +} + +export { Input }; diff --git a/src/components/ui/label.tsx b/src/components/ui/label.tsx new file mode 100644 index 0000000..39ffdee --- /dev/null +++ b/src/components/ui/label.tsx @@ -0,0 +1,21 @@ +import { cn } from "cn"; +import { Label as LabelPrimitive } from "radix-ui"; +import type * as React from "react"; + +function Label({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { Label }; diff --git a/src/components/ui/select.tsx b/src/components/ui/select.tsx new file mode 100644 index 0000000..bf03c5b --- /dev/null +++ b/src/components/ui/select.tsx @@ -0,0 +1,187 @@ +import { cn } from "cn"; +import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"; +import { Select as SelectPrimitive } from "radix-ui"; +import type * as React from "react"; + +function Select({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectGroup({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectValue({ + ...props +}: React.ComponentProps) { + return ; +} + +function SelectTrigger({ + className, + size = "default", + children, + ...props +}: React.ComponentProps & { + size?: "sm" | "default"; +}) { + return ( + + {children} + + + + + ); +} + +function SelectContent({ + className, + children, + position = "item-aligned", + align = "center", + ...props +}: React.ComponentProps) { + return ( + + + + + {children} + + + + + ); +} + +function SelectLabel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SelectItem({ + className, + children, + ...props +}: React.ComponentProps) { + return ( + + + + + + + {children} + + ); +} + +function SelectSeparator({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function SelectScrollUpButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +function SelectScrollDownButton({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + ); +} + +export { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectLabel, + SelectScrollDownButton, + SelectScrollUpButton, + SelectSeparator, + SelectTrigger, + SelectValue, +}; diff --git a/src/global.css b/src/global.css new file mode 100644 index 0000000..4577c3c --- /dev/null +++ b/src/global.css @@ -0,0 +1,91 @@ +:root { + --background: #fafafa; + --foreground: #09090b; + + --card: #ffffff; + --card-foreground: #09090b; + + --popover: #ffffff; + --popover-foreground: #09090b; + + --primary: #7c3aed; + --primary-foreground: #ffffff; + + --secondary: #f4f4f5; + --secondary-foreground: #18181b; + + --muted: #f4f4f5; + --muted-foreground: #71717a; + + --accent: #f4f4f5; + --accent-foreground: #18181b; + + --destructive: #ef4444; + --destructive-foreground: #ffffff; + + --border: #e4e4e7; + --input: #e4e4e7; + --ring: #7c3aed; + + --radius: 0.75rem; + + --chart-1: #7c3aed; + --chart-2: #8b5cf6; + --chart-3: #a78bfa; + --chart-4: #c4b5fd; + --chart-5: #ddd6fe; + + --sidebar: #ffffff; + --sidebar-foreground: #09090b; + --sidebar-primary: #7c3aed; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #f4f4f5; + --sidebar-accent-foreground: #18181b; + --sidebar-border: #e4e4e7; + --sidebar-ring: #7c3aed; +} + +.dark { + --background: #09090b; + --foreground: #f4f4f5; + + --card: #121215; + --card-foreground: #f4f4f5; + + --popover: #121215; + --popover-foreground: #f4f4f5; + + --primary: #7c3aed; + --primary-foreground: #ffffff; + + --secondary: #18181b; + --secondary-foreground: #f4f4f5; + + --muted: #18181b; + --muted-foreground: #a1a1aa; + + --accent: #18181b; + --accent-foreground: #f4f4f5; + + --destructive: #ef4444; + --destructive-foreground: #ffffff; + + --border: #27272a; + --input: #27272a; + --ring: #8b5cf6; + + --chart-1: #8b5cf6; + --chart-2: #7c3aed; + --chart-3: #a78bfa; + --chart-4: #c4b5fd; + --chart-5: #ddd6fe; + + --sidebar: #121215; + --sidebar-foreground: #f4f4f5; + --sidebar-primary: #7c3aed; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #18181b; + --sidebar-accent-foreground: #f4f4f5; + --sidebar-border: #27272a; + --sidebar-ring: #8b5cf6; +} diff --git a/src/lib/db.ts b/src/lib/db.ts new file mode 100644 index 0000000..27297e3 --- /dev/null +++ b/src/lib/db.ts @@ -0,0 +1,68 @@ +import mongoose from "mongoose"; +import { ensureSuperAdmin } from "../server/superAdmin.ts"; + +interface MongooseCache { + conn: typeof mongoose | null; + promise: Promise | null; +} + +declare global { + var _mongooseCache: MongooseCache | undefined; +} + +const cached: MongooseCache = globalThis._mongooseCache ?? { + conn: null, + promise: null, +}; + +if (!globalThis._mongooseCache) { + globalThis._mongooseCache = cached; +} + +export async function connectDB(): Promise { + const uri = process.env.MONGODB_URI; + + if (!uri) { + throw new Error( + "Please define the MONGODB_URI environment variable in .env", + ); + } + + if (cached.conn) { + return cached.conn; + } + + if (!cached.promise) { + const opts = { + bufferCommands: false, + }; + + cached.promise = mongoose + .connect(uri, opts) + .then(async (mongooseInstance) => { + console.log("MongoDB is connected"); + try { + await ensureSuperAdmin(); + } catch (err) { + console.warn("Could not ensure super admin upon connection:", err); + } + return mongooseInstance; + }) + .catch((error) => { + console.error("MongoDB connection error:", error); + cached.promise = null; + throw error; + }); + } + + try { + cached.conn = await cached.promise; + } catch (error) { + cached.promise = null; + throw error; + } + + return cached.conn; +} + +export default connectDB; diff --git a/src/lib/utils.ts b/src/lib/utils.ts index abba253..d39aff9 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -1,7 +1,7 @@ -import type { ClassValue } from 'clsx' -import { clsx } from 'clsx' -import { twMerge } from 'tailwind-merge' +import type { ClassValue } from "clsx"; +import { clsx } from "clsx"; +import { twMerge } from "tailwind-merge"; export function cn(...inputs: ClassValue[]) { - return twMerge(clsx(inputs)) + return twMerge(clsx(inputs)); } diff --git a/src/models/College.ts b/src/models/College.ts new file mode 100644 index 0000000..dfc356f --- /dev/null +++ b/src/models/College.ts @@ -0,0 +1,65 @@ +import mongoose, { type Document, type Model, Schema } from "mongoose"; + +export type CollegeStatus = "PENDING" | "ACTIVE" | "REJECTED"; + +export interface ICollege { + name: string; + adminId?: mongoose.Types.ObjectId | string; + adminName?: string; + adminEmail?: string; + status: CollegeStatus; + appliedAt?: Date; + reviewedAt?: Date; + rejectionReason?: string; + createdAt?: Date; + updatedAt?: Date; +} + +export interface ICollegeDocument extends ICollege, Document {} + +const collegeSchema = new Schema( + { + name: { + type: String, + required: [true, "College name is required"], + unique: true, + trim: true, + }, + adminId: { + type: Schema.Types.ObjectId, + ref: "User", + }, + adminName: { + type: String, + default: "", + }, + adminEmail: { + type: String, + default: "", + }, + status: { + type: String, + enum: ["PENDING", "ACTIVE", "REJECTED"], + default: "PENDING", + }, + appliedAt: { + type: Date, + default: Date.now, + }, + reviewedAt: { + type: Date, + }, + rejectionReason: { + type: String, + }, + }, + { + timestamps: true, + }, +); + +export const College: Model = + (mongoose.models.College as Model) || + mongoose.model("College", collegeSchema); + +export default College; diff --git a/src/models/User.ts b/src/models/User.ts new file mode 100644 index 0000000..9766292 --- /dev/null +++ b/src/models/User.ts @@ -0,0 +1,80 @@ +import mongoose, { type Document, type Model, Schema } from "mongoose"; + +export type UserRole = "STUDENT" | "COLLEGE_ADMIN" | "SUPER_ADMIN"; +export type UserStatus = "ACTIVE" | "PENDING_REVIEW" | "REJECTED" | "SUSPENDED"; + +export interface IUser { + name: string; + email: string; + password: string; + role: UserRole; + collegeName: string; + collegeId?: string; + status: UserStatus; + reviewedBy?: string; + reviewedAt?: Date; + rejectionReason?: string; + createdAt?: Date; + updatedAt?: Date; +} + +export interface IUserDocument extends IUser, Document {} + +const userSchema = new Schema( + { + name: { + type: String, + required: [true, "Name is required"], + }, + email: { + type: String, + required: [true, "Email is required"], + unique: true, + lowercase: true, + trim: true, + }, + password: { + type: String, + required: [true, "Password is required"], + }, + role: { + type: String, + enum: ["STUDENT", "COLLEGE_ADMIN", "SUPER_ADMIN"], + default: "STUDENT", + }, + collegeName: { + type: String, + required: [true, "College name is required"], + }, + collegeId: { + type: String, + required: false, + }, + status: { + type: String, + enum: ["ACTIVE", "PENDING_REVIEW", "REJECTED", "SUSPENDED"], + default: "ACTIVE", + }, + reviewedBy: { + type: String, + required: false, + }, + reviewedAt: { + type: Date, + required: false, + }, + rejectionReason: { + type: String, + required: false, + }, + }, + { + timestamps: true, + }, +); + +export const User: Model = + (mongoose.models.User as Model) || + mongoose.model("User", userSchema); + +export default User; diff --git a/src/routeTree.gen.ts b/src/routeTree.gen.ts index dceedff..6236b6d 100644 --- a/src/routeTree.gen.ts +++ b/src/routeTree.gen.ts @@ -10,33 +10,73 @@ import { Route as rootRouteImport } from './routes/__root' import { Route as IndexRouteImport } from './routes/index' +import { Route as AdminRouteImport } from './routes/admin' +import { Route as DashboardRouteImport } from './routes/dashboard' +import { Route as LoginRouteImport } from './routes/login' +import { Route as SignupRouteImport } from './routes/signup' const IndexRoute = IndexRouteImport.update({ id: '/', path: '/', getParentRoute: () => rootRouteImport, } as any) +const AdminRoute = AdminRouteImport.update({ + id: '/admin', + path: '/admin', + getParentRoute: () => rootRouteImport, +} as any) +const DashboardRoute = DashboardRouteImport.update({ + id: '/dashboard', + path: '/dashboard', + getParentRoute: () => rootRouteImport, +} as any) +const LoginRoute = LoginRouteImport.update({ + id: '/login', + path: '/login', + getParentRoute: () => rootRouteImport, +} as any) +const SignupRoute = SignupRouteImport.update({ + id: '/signup', + path: '/signup', + getParentRoute: () => rootRouteImport, +} as any) export interface FileRoutesByFullPath { '/': typeof IndexRoute + '/admin': typeof AdminRoute + '/dashboard': typeof DashboardRoute + '/login': typeof LoginRoute + '/signup': typeof SignupRoute } export interface FileRoutesByTo { '/': typeof IndexRoute + '/admin': typeof AdminRoute + '/dashboard': typeof DashboardRoute + '/login': typeof LoginRoute + '/signup': typeof SignupRoute } export interface FileRoutesById { __root__: typeof rootRouteImport '/': typeof IndexRoute + '/admin': typeof AdminRoute + '/dashboard': typeof DashboardRoute + '/login': typeof LoginRoute + '/signup': typeof SignupRoute } export interface FileRouteTypes { fileRoutesByFullPath: FileRoutesByFullPath - fullPaths: '/' + fullPaths: '/' | '/admin' | '/dashboard' | '/login' | '/signup' fileRoutesByTo: FileRoutesByTo - to: '/' - id: '__root__' | '/' + to: '/' | '/admin' | '/dashboard' | '/login' | '/signup' + id: '__root__' | '/' | '/admin' | '/dashboard' | '/login' | '/signup' fileRoutesById: FileRoutesById } export interface RootRouteChildren { IndexRoute: typeof IndexRoute + AdminRoute: typeof AdminRoute + DashboardRoute: typeof DashboardRoute + LoginRoute: typeof LoginRoute + SignupRoute: typeof SignupRoute } declare module '@tanstack/react-router' { @@ -48,11 +88,43 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof IndexRouteImport parentRoute: typeof rootRouteImport } + '/admin': { + id: '/admin' + path: '/admin' + fullPath: '/admin' + preLoaderRoute: typeof AdminRouteImport + parentRoute: typeof rootRouteImport + } + '/dashboard': { + id: '/dashboard' + path: '/dashboard' + fullPath: '/dashboard' + preLoaderRoute: typeof DashboardRouteImport + parentRoute: typeof rootRouteImport + } + '/login': { + id: '/login' + path: '/login' + fullPath: '/login' + preLoaderRoute: typeof LoginRouteImport + parentRoute: typeof rootRouteImport + } + '/signup': { + id: '/signup' + path: '/signup' + fullPath: '/signup' + preLoaderRoute: typeof SignupRouteImport + parentRoute: typeof rootRouteImport + } } } const rootRouteChildren: RootRouteChildren = { IndexRoute: IndexRoute, + AdminRoute: AdminRoute, + DashboardRoute: DashboardRoute, + LoginRoute: LoginRoute, + SignupRoute: SignupRoute, } export const routeTree = rootRouteImport ._addFileChildren(rootRouteChildren) diff --git a/src/router.tsx b/src/router.tsx index e7b1c4d..0b1d0fb 100644 --- a/src/router.tsx +++ b/src/router.tsx @@ -1,19 +1,19 @@ -import { createRouter as createTanStackRouter } from '@tanstack/react-router' -import { routeTree } from './routeTree.gen' +import { createRouter as createTanStackRouter } from "@tanstack/react-router"; +import { routeTree } from "./routeTree.gen"; export function getRouter() { - const router = createTanStackRouter({ - routeTree, - scrollRestoration: true, - defaultPreload: 'intent', - defaultPreloadStaleTime: 0, - }) + const router = createTanStackRouter({ + routeTree, + scrollRestoration: true, + defaultPreload: "intent", + defaultPreloadStaleTime: 0, + }); - return router + return router; } -declare module '@tanstack/react-router' { - interface Register { - router: ReturnType - } +declare module "@tanstack/react-router" { + interface Register { + router: ReturnType; + } } diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index f4b63b1..7b57477 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -1,54 +1,383 @@ -import { HeadContent, Scripts, createRootRoute } from '@tanstack/react-router' -import { TanStackRouterDevtoolsPanel } from '@tanstack/react-router-devtools' -import { TanStackDevtools } from '@tanstack/react-devtools' +import { TanStackDevtools } from "@tanstack/react-devtools"; +import { + createRootRoute, + HeadContent, + Link, + Outlet, + Scripts, + useRouter, +} from "@tanstack/react-router"; +import { TanStackRouterDevtoolsPanel } from "@tanstack/react-router-devtools"; +import { createServerFn } from "@tanstack/react-start"; +import { + Briefcase, + LayoutDashboard, + Loader2, + LogOut, + Menu, + Shield, + X, +} from "lucide-react"; +import { useState } from "react"; +import { Badge } from "../components/ui/badge"; +import { Button } from "../components/ui/button"; +import { connectDB } from "../lib/db"; +import { type AuthUser, getCurrentUserFn, logOutFn } from "../server/authFns"; +import { ensureSuperAdmin } from "../server/superAdmin"; -import appCss from '../styles.css?url' +import appCss from "../styles.css?url"; + +const ensureDbConnected = createServerFn().handler(async () => { + try { + await connectDB(); + await ensureSuperAdmin(); + } catch (err) { + console.warn("MongoDB connection deferred in root:", err); + } +}); export const Route = createRootRoute({ - head: () => ({ - meta: [ - { - charSet: 'utf-8', - }, - { - name: 'viewport', - content: 'width=device-width, initial-scale=1', - }, - { - title: 'TanStack Start Starter', - }, - ], - links: [ - { - rel: 'stylesheet', - href: appCss, - }, - ], - }), - shellComponent: RootDocument, -}) + beforeLoad: async () => { + await ensureDbConnected(); + const user = await getCurrentUserFn(); + return { user }; + }, + loader: async ({ context }) => { + return { user: context.user }; + }, + head: () => ({ + meta: [ + { + charSet: "utf-8", + }, + { + name: "viewport", + content: "width=device-width, initial-scale=1", + }, + { + title: "CampusGigs - University Freelance & Gigs Marketplace", + }, + ], + links: [ + { + rel: "stylesheet", + href: appCss, + }, + ], + }), + component: RootComponent, + shellComponent: RootDocument, +}); + +function Navbar({ user }: { user: AuthUser | null }) { + const router = useRouter(); + const [loggingOut, setLoggingOut] = useState(false); + const [mobileMenuOpen, setMobileMenuOpen] = useState(false); + + const handleLogout = async () => { + try { + setLoggingOut(true); + await logOutFn(); + await router.invalidate(); + await router.navigate({ to: "/login" }); + } catch (err) { + console.error("Logout error:", err); + } finally { + setLoggingOut(false); + setMobileMenuOpen(false); + } + }; + + return ( +
+
+
+ {/* Brand Logo & Name */} + +
+ +
+
+ + CampusGigs + + + Campus Marketplace + +
+ + + {/* Desktop Navigation Links */} + + + {/* Mobile Menu Toggle */} +
+ +
+
+
+ + {/* Mobile Dropdown Menu */} + {mobileMenuOpen && ( +
+ setMobileMenuOpen(false)} + className="block px-3 py-2 rounded-lg text-base font-medium text-foreground hover:bg-secondary transition-colors" + > + Explore + + + {user ? ( + <> +
+
+
+ {user.name.charAt(0).toUpperCase()} +
+
+
+ {user.name} +
+
+ {user.collegeName} +
+
+
+ + {user.role} + +
+ + {user.role === "SUPER_ADMIN" && ( + + )} + + + + + + ) : ( +
+ + +
+ )} +
+ )} +
+ ); +} + +function Footer() { + return ( +
+
+
+
+ +
+ CampusGigs + + © {new Date().getFullYear()} All rights reserved. + +
+
+ + Privacy Policy + + + Terms of Service + + + Campus Guidelines + +
+
+
+ ); +} + +function RootComponent() { + const { user } = Route.useRouteContext(); + + return ( +
+ +
+ +
+
+
+ ); +} function RootDocument({ children }: { children: React.ReactNode }) { - return ( - - - - - - {children} - , - }, - ]} - /> - - - - ) + return ( + + + + + + {children} + , + }, + ]} + /> + + + + ); } diff --git a/src/routes/admin.tsx b/src/routes/admin.tsx new file mode 100644 index 0000000..702eaac --- /dev/null +++ b/src/routes/admin.tsx @@ -0,0 +1,564 @@ +import { createFileRoute, redirect, useRouter } from "@tanstack/react-router"; +import { + AlertCircle, + Building2, + Check, + CheckCircle2, + Clock, + Inbox, + Loader2, + Mail, + RotateCw, + Shield, + User, + X, + XCircle, +} from "lucide-react"; +import { useState } from "react"; +import { Badge } from "../components/ui/badge"; +import { Button } from "../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "../components/ui/card"; +import { + type CollegeApplicationItem, + getCollegeApplicationsFn, + reviewCollegeApplicationFn, +} from "../server/adminFns"; +import { getCurrentUserFn } from "../server/authFns"; + +export const Route = createFileRoute("/admin")({ + beforeLoad: async () => { + const user = await getCurrentUserFn(); + if (!user) { + throw redirect({ + to: "/login", + }); + } + if (user.role !== "SUPER_ADMIN") { + throw redirect({ + to: "/dashboard", + }); + } + return { user }; + }, + loader: async () => { + const applications = await getCollegeApplicationsFn(); + return { applications }; + }, + component: AdminDashboardPage, +}); + +function AdminDashboardPage() { + const router = useRouter(); + const { user } = Route.useRouteContext(); + const { applications } = Route.useLoaderData(); + + const [filter, setFilter] = useState<"PENDING" | "ACTIVE" | "ALL">("PENDING"); + const [processingAction, setProcessingAction] = useState(null); + const [feedback, setFeedback] = useState<{ + type: "success" | "error"; + message: string; + } | null>(null); + + const handleReview = async ( + collegeId: string, + action: "ACCEPT" | "REJECT", + collegeName: string, + ) => { + const actionKey = `${collegeId}-${action}`; + try { + setProcessingAction(actionKey); + setFeedback(null); + + const res = await reviewCollegeApplicationFn({ + data: { + collegeId, + action, + }, + }); + + setFeedback({ + type: "success", + message: + res.message || + `Successfully ${action === "ACCEPT" ? "accepted" : "rejected"} portal for ${collegeName}.`, + }); + + // Invalidate router cache to refresh applications from loader + await router.invalidate(); + } catch (err: unknown) { + const message = + err instanceof Error ? err.message : "Failed to review application"; + setFeedback({ + type: "error", + message, + }); + } finally { + setProcessingAction(null); + } + }; + + // Metrics + const totalApplications = applications.length; + const pendingCount = applications.filter( + (a) => a.status === "PENDING", + ).length; + const activeCount = applications.filter((a) => a.status === "ACTIVE").length; + const rejectedCount = applications.filter( + (a) => a.status === "REJECTED", + ).length; + + // Filtered list + const filteredApplications = applications.filter((app) => { + if (filter === "PENDING") return app.status === "PENDING"; + if (filter === "ACTIVE") return app.status === "ACTIVE"; + return true; + }); + + const formatAppliedDate = (dateStr?: string) => { + if (!dateStr) return "Recently"; + try { + const date = new Date(dateStr); + return date.toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + }); + } catch { + return dateStr; + } + }; + + return ( +
+
+ {/* Header Banner */} + + +
+
+ +
+
+
+

+ Super Admin Portal Console +

+ + Platform Authority + +
+

+ Manage college portal applications, campus tenants, and + institutional access +

+
+
+ +
+
+
+ {user.name} +
+
+ {user.email} +
+
+ +
+
+
+ + {/* Feedback Alerts */} + {feedback && ( +
+ {feedback.type === "success" ? ( + + ) : ( + + )} +
{feedback.message}
+ +
+ )} + + {/* Overview Metrics Cards */} +
+ + + + Total Applications + +
+ +
+
+ +
+ {totalApplications} +
+

+ Campus tenant requests +

+
+
+ + {/* Pending Review with Alert Highlight */} + 0 + ? "border-primary/50 bg-primary/[0.02] ring-1 ring-primary/20" + : "border-border" + }`} + > + + + Pending Review + {pendingCount > 0 && ( + + )} + +
+ +
+
+ +
+ {pendingCount} + {pendingCount > 0 && ( + + Action needed + + )} +
+

+ Awaiting activation +

+
+
+ + + + + Active Campus Portals + +
+ +
+
+ +
+ {activeCount} +
+

+ Operational portals +

+
+
+ + + + + Rejected Applications + +
+ +
+
+ +
+ {rejectedCount} +
+

+ Declined requests +

+
+
+
+ + {/* Filter Tabs and Applications List */} + + +
+ + College Portal Applications + + + Review institutional credentials before permitting administrator + access + +
+ + {/* Filter Tabs */} +
+ + + + + +
+
+ + + {filteredApplications.length === 0 ? ( +
+
+ +
+

+ No applications found +

+

+ {filter === "PENDING" + ? "There are currently no college portal applications awaiting review." + : "No college applications match the selected filter."} +

+
+ ) : ( +
+ {filteredApplications.map((app: CollegeApplicationItem) => { + const isAccepting = processingAction === `${app._id}-ACCEPT`; + const isRejecting = processingAction === `${app._id}-REJECT`; + const isBusy = isAccepting || isRejecting; + + return ( +
+ {/* Application Details */} +
+
+

+ + {app.name} +

+ + {/* Status Badge */} + {app.status === "PENDING" && ( + + + Pending Review + + )} + + {app.status === "ACTIVE" && ( + + + Active Portal + + )} + + {app.status === "REJECTED" && ( + + + Rejected + + )} +
+ + {/* Admin contact and metadata */} +
+
+ + + {app.adminName || "College Administrator"} + +
+ + {app.adminEmail && ( +
+ + {app.adminEmail} +
+ )} + +
+ + + Applied {formatAppliedDate(app.appliedAt)} + +
+
+
+ + {/* Action Controls */} +
+ {app.status === "PENDING" ? ( + <> + + + + + ) : app.status === "ACTIVE" ? ( +
+ + + Portal live + + +
+ ) : ( +
+ + + Application rejected + + +
+ )} +
+
+ ); + })} +
+ )} +
+
+
+
+ ); +} diff --git a/src/routes/dashboard.tsx b/src/routes/dashboard.tsx new file mode 100644 index 0000000..ad9dad3 --- /dev/null +++ b/src/routes/dashboard.tsx @@ -0,0 +1,1259 @@ +import { + createFileRoute, + Link, + redirect, + useRouter, +} from "@tanstack/react-router"; +import { + AlertCircle, + ArrowRight, + Award, + Briefcase, + Building2, + Calendar, + CheckCircle2, + Clock, + DollarSign, + ExternalLink, + GraduationCap, + IdCard, + Inbox, + Loader2, + LogOut, + Mail, + Plus, + School, + ShieldAlert, + ShieldCheck, + TrendingUp, + User as UserIcon, + Users, + X, + XCircle, +} from "lucide-react"; +import { useState } from "react"; +import { Badge } from "../components/ui/badge"; +import { Button } from "../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "../components/ui/card"; +import { Input } from "../components/ui/input"; +import { Label } from "../components/ui/label"; +import { getCurrentUserFn, logOutFn } from "../server/authFns"; +import { + getStudentApplicationsFn, + reviewStudentApplicationFn, + type StudentApplicationItem, +} from "../server/collegeAdminFns"; + +export const Route = createFileRoute("/dashboard")({ + beforeLoad: async () => { + const user = await getCurrentUserFn(); + if (!user) { + throw redirect({ + to: "/login", + }); + } + return { user }; + }, + loader: async ({ context }) => { + let studentApplications: StudentApplicationItem[] = []; + if ( + context.user.role === "COLLEGE_ADMIN" || + context.user.role === "SUPER_ADMIN" + ) { + try { + studentApplications = await getStudentApplicationsFn(); + } catch (err) { + console.error("Failed to load student applications:", err); + } + } + return { user: context.user, studentApplications }; + }, + component: DashboardPage, +}); + +function DashboardPage() { + const router = useRouter(); + const { user, studentApplications = [] } = Route.useLoaderData(); + const [loggingOut, setLoggingOut] = useState(false); + + const handleLogout = async () => { + try { + setLoggingOut(true); + await logOutFn(); + await router.invalidate(); + await router.navigate({ to: "/login" }); + } catch (err) { + console.error("Logout error:", err); + } finally { + setLoggingOut(false); + } + }; + + const isStudent = user.role === "STUDENT"; + const isCollegeAdmin = user.role === "COLLEGE_ADMIN"; + const isSuperAdmin = user.role === "SUPER_ADMIN"; + + // Student applications filtering & processing state + const pendingStudents = studentApplications.filter( + (s) => s.status === "PENDING_REVIEW", + ); + const enrolledStudents = studentApplications.filter( + (s) => s.status === "ACTIVE", + ); + const rejectedStudents = studentApplications.filter( + (s) => s.status === "REJECTED", + ); + const totalApplicantsCount = studentApplications.length; + + const [filterTab, setFilterTab] = useState< + "PENDING" | "ENROLLED" | "REJECTED" | "ALL" + >(() => (pendingStudents.length > 0 ? "PENDING" : "ALL")); + + const [processingStudentId, setProcessingStudentId] = useState( + null, + ); + const [processingAction, setProcessingAction] = useState< + "APPROVE" | "REJECT" | null + >(null); + const [rejectingStudentId, setRejectingStudentId] = useState( + null, + ); + const [rejectionReasonInput, setRejectionReasonInput] = useState(""); + const [actionFeedback, setActionFeedback] = useState<{ + type: "success" | "error"; + message: string; + } | null>(null); + + const handleReviewStudent = async ( + studentId: string, + action: "APPROVE" | "REJECT", + rejectionReason?: string, + ) => { + try { + setProcessingStudentId(studentId); + setProcessingAction(action); + setActionFeedback(null); + + const res = await reviewStudentApplicationFn({ + data: { + studentId, + action, + rejectionReason: rejectionReason?.trim() || undefined, + }, + }); + + setActionFeedback({ + type: "success", + message: + res.message || + (action === "APPROVE" + ? "Student application approved and enrolled." + : "Student application rejected."), + }); + + if (rejectingStudentId === studentId) { + setRejectingStudentId(null); + setRejectionReasonInput(""); + } + + await router.invalidate(); + } catch (err: unknown) { + const message = + err instanceof Error + ? err.message + : "Failed to process application. Please try again."; + setActionFeedback({ + type: "error", + message, + }); + } finally { + setProcessingStudentId(null); + setProcessingAction(null); + } + }; + + const getInitials = (name: string) => { + return ( + name + .trim() + .split(/\s+/) + .map((part) => part[0]) + .slice(0, 2) + .join("") + .toUpperCase() || "ST" + ); + }; + + const displayedStudents = studentApplications.filter((student) => { + if (filterTab === "PENDING") return student.status === "PENDING_REVIEW"; + if (filterTab === "ENROLLED") return student.status === "ACTIVE"; + if (filterTab === "REJECTED") return student.status === "REJECTED"; + return true; + }); + + return ( +
+
+ {/* Prominent Super Admin Console Card at Top */} + {isSuperAdmin && ( + + +
+
+ +
+
+
+

+ Super Admin Console +

+ + System Master + +
+

+ Review and approve college portal applications, activate + university workspaces, and oversee campus administrator + requests. +

+
+
+ +
+
+ )} + + {/* Top Welcome Card */} + + +
+
+ {user.name.charAt(0).toUpperCase()} +
+
+
+

+ Welcome back, {user.name} +

+ + + Verified .edu + +
+

+ + + {user.collegeName} + + • + + {isStudent + ? "Student Freelancer" + : isSuperAdmin + ? "Super Administrator" + : "Campus Administrator"} + +

+
+
+ +
+ +
+
+
+ + {/* College Admin Active Campus Portal Management Hub */} + {isCollegeAdmin && ( + + +
+
+
+
+ +
+ + Campus Portal: {user.collegeName} + + + + ACTIVE / APPROVED + +
+ + Official institutional portal approved by CampusGigs Super + Admin. + +
+
+
+ + {/* Quick stats for campus gigs, student registrations, and moderation */} +
+ {/* Campus Gigs */} +
+
+ Campus Gigs + +
+
+ 38 Active +
+

+ 14 faculty postings • 24 student requests +

+
+ + {/* Student Registrations */} +
+
+ Student Registrations + +
+
+ {enrolledStudents.length} Verified +
+

+ {pendingStudents.length > 0 + ? `+${pendingStudents.length} registrations awaiting review` + : "All registrations reviewed"} +

+
+ + {/* Moderation */} +
+
+ Moderation + +
+
+ 0 Flagged +
+

+ All campus listings compliant & active +

+
+
+ + {/* Actions for college admin */} +
+ + +
+
+
+ )} + + {/* College Admin: Student Applications & Campus Admission Management */} + {isCollegeAdmin && ( + + +
+
+
+
+ +
+ + Student Applications & Campus Admission + + {pendingStudents.length > 0 && ( + + + + + + {pendingStudents.length} Awaiting Verification + + )} +
+ + Review and verify student freelancer admission requests for{" "} + + {user.collegeName} + + . + +
+
+
+ + + {/* Action Feedback Banner */} + {actionFeedback && ( +
+
+ {actionFeedback.type === "success" ? ( + + ) : ( + + )} + + {actionFeedback.message} + +
+ +
+ )} + + {/* Metric Counters */} +
+ {/* Total Applicants */} + + + {/* Pending Review with Alert Indicator */} + + + {/* Enrolled / Active Students */} + + + {/* Rejected Applications */} + +
+ + {/* Filter Tabs */} +
+ + + + + + + +
+ + {/* Student List or Empty State */} + {displayedStudents.length === 0 ? ( +
+
+ +
+
+

+ {filterTab === "PENDING" + ? "No Applications Pending Review" + : filterTab === "ENROLLED" + ? "No Enrolled Students Yet" + : filterTab === "REJECTED" + ? "No Rejected Applications" + : "No Student Applications Recorded"} +

+

+ {filterTab === "PENDING" + ? "Great job! All incoming student verification requests for your campus have been reviewed." + : filterTab === "ENROLLED" + ? "Students accepted into your campus portal will appear here once approved." + : filterTab === "REJECTED" + ? "Applications rejected during verification will be logged here." + : "When students register choosing your university, their applications will appear here for review."} +

+
+
+ ) : ( +
+ {displayedStudents.map((student) => { + const isPending = student.status === "PENDING_REVIEW"; + const isActive = student.status === "ACTIVE"; + const isRejected = student.status === "REJECTED"; + const isCurrentProcessing = + processingStudentId === student.id; + + return ( +
+
+ {/* Student Info & Avatar */} +
+
+ {getInitials(student.name)} +
+
+
+

+ {student.name} +

+ + {/* Status Badges */} + {isPending && ( + + + Awaiting Review + + )} + {isActive && ( + + + Active / Enrolled + + )} + {isRejected && ( + + + Rejected + + )} +
+ +
+ + + {student.email} + + • + + + {isActive && student.reviewedAt + ? `Enrolled ${new Date( + student.reviewedAt, + ).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + })}` + : student.createdAt + ? `Submitted ${new Date( + student.createdAt, + ).toLocaleDateString("en-US", { + month: "short", + day: "numeric", + year: "numeric", + })}` + : "Recently submitted"} + +
+
+
+ + {/* Pending Action Buttons */} + {isPending && ( +
+ + + +
+ )} +
+ + {/* Rejection Reason Note if Student is Rejected */} + {isRejected && student.rejectionReason && ( +
+ +
+ + Rejection Reason:{" "} + + {student.rejectionReason} +
+
+ )} + + {/* Inline Rejection Prompt */} + {rejectingStudentId === student.id && ( +
+
+ + + Visible to applicant + +
+ + setRejectionReasonInput(e.target.value) + } + placeholder="e.g. Unverified enrollment credentials, invalid student ID number..." + className="h-9 text-xs bg-background" + /> +
+ + +
+
+ )} +
+ ); + })} +
+ )} +
+
+ )} + + {/* Overview Metrics Tiles using Shadcn Cards */} +
+ + + + {isStudent ? "Active Gigs" : "Campus Postings"} + +
+ +
+
+ +
+ {isStudent ? "14" : "52"} +
+

+ {isStudent ? "+3 posted today" : "8 filled this week"} +

+
+
+ + + + + {isStudent ? "Applications" : "Student Applicants"} + +
+ +
+
+ +
+ {isStudent ? "5" : totalApplicantsCount} +
+

+ {isStudent + ? "2 under review" + : `${pendingStudents.length} pending review`} +

+
+
+ + + + + {isStudent ? "Total Earnings" : "Disbursed Budget"} + +
+ +
+
+ +
+ {isStudent ? "$1,450" : "$18,200"} +
+

+ {isStudent ? "Semester to date" : "Escrow backed"} +

+
+
+ + + + + {isStudent ? "Campus Rating" : "Placement Rate"} + +
+ +
+
+ +
+ {isStudent ? "4.9 ★" : "96%"} +
+

+ {isStudent + ? "From 9 completed gigs" + : "Department satisfaction"} +

+
+
+
+ + {/* Two Column Grid: User Profile Card & Role-Specific Activity */} +
+ {/* User Profile Card */} + + +
+ + + Profile Information + + + {isStudent ? ( + <> + + Student + + ) : isSuperAdmin ? ( + <> + + Super Admin + + ) : ( + <> + + College Admin + + )} + +
+ + Verified campus identity details + +
+ + +
+ + Full Name + +

+ {user.name} +

+
+ +
+ + Campus Email + +

+ + {user.email} +

+
+ +
+ + College or University + +

+ + {user.collegeName} +

+
+ +
+ + Assigned Role + +
+ + {isStudent + ? "Student Freelancer" + : isSuperAdmin + ? "Super Administrator" + : "College Administrator"} + +
+
+ +
+ + User Identifier + +

+ + {user.id} +

+
+ +
+ + Session secured with HTTP-only JWT +
+
+
+ + {/* Main Content Area */} +
+ {/* Role Activity Card */} + + +
+ + {isStudent + ? "Featured Campus Opportunities" + : isSuperAdmin + ? "Super Admin Management Panel" + : "Campus Administration Center"} + + + {isStudent + ? "Projects tailored to your skills & campus schedule" + : isSuperAdmin + ? "Approve portal applications and supervise campus networks" + : "Post gigs, verify student credentials, and manage budgets"} + +
+ {isStudent && ( + + )} +
+ + + {isStudent ? ( +
+ {/* Gig 1 */} +
+
+
+ + UI/UX Redesign for CS Department Portal + + + $450 Fixed + +
+
+ Dept of Computer Science + • + + Due in 2 weeks + +
+
+ +
+ + {/* Gig 2 */} +
+
+
+ + Annual Hackathon Event Photographer & Editor + + + $25 / hr + +
+
+ Student Union Events + • + + This Weekend + +
+
+ +
+ + {/* Gig 3 */} +
+
+
+ + Mobile App Quality Assurance & Beta Tester + + + $20 / hr + +
+
+ Campus Innovation Lab + • + + Flexible Hours + +
+
+ +
+
+ ) : isSuperAdmin ? ( +
+
+

+ Platform Overview: CampusGigs HQ +

+

+ You have global platform oversight. College portals + require your direct authorization before they are + activated for student registrations and gig postings. +

+
+ +
+ +
+
+ ) : ( +
+
+

+ Admin Control Center: {user.collegeName} +

+

+ You have elevated campus management privileges. Post new + university gigs, review submitted student applications, + and authorize milestone disbursements. +

+
+ +
+ + +
+
+ )} +
+
+
+
+
+
+ ); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 6678649..7886d74 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -1,14 +1,289 @@ -import { createFileRoute } from '@tanstack/react-router' - -export const Route = createFileRoute('/')({ component: Home }) - -function Home() { - return ( -
-

Welcome to TanStack Start

-

- Edit src/routes/index.tsx to get started. -

-
- ) +import { createFileRoute, Link } from "@tanstack/react-router"; +import { + ArrowRight, + Building2, + CheckCircle2, + GraduationCap, + LayoutDashboard, + Lock, + ShieldCheck, + Sparkles, + Wallet, +} from "lucide-react"; +import { Badge } from "../components/ui/badge"; +import { Button } from "../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "../components/ui/card"; +import { getCurrentUserFn } from "../server/authFns"; + +export const Route = createFileRoute("/")({ + loader: async () => { + const user = await getCurrentUserFn(); + return { user }; + }, + component: HomePage, +}); + +function HomePage() { + const { user } = Route.useLoaderData(); + + return ( +
+ {/* Minimalist Grayscale Hero Section */} +
+
+
+ + + Campus Freelance & Gig Marketplace + +
+ +
+

+ Work campus gigs, earn money + , and elevate your resume. +

+

+ CampusGigs connects university students with campus departments, + student organizations, and local initiatives for flexible, + well-compensated project work. +

+
+ + {/* Vibrant Purple Primary CTA Buttons */} +
+ {user ? ( +
+ +

+ Signed in as{" "} + + {user.name} + {" "} + ({user.collegeName}) +

+
+ ) : ( + <> + + + + )} +
+ + {/* Trust Badges */} +
+
+ + Verified .edu Emails +
+
+ + JWT Secure Auth +
+
+ + Escrow-Protected Payouts +
+
+
+
+ + {/* Features Grid using Shadcn Cards */} +
+
+
+ + Built for Higher Ed + +

+ Everything your campus community needs +

+

+ A streamlined platform created specifically to support student + schedules and college administrative policies. +

+
+ +
+ {/* Card 1: Students */} + + +
+ +
+ + For Student Freelancers + + + Convert your software, writing, design, research, or event + skills into immediate income without sacrificing academic + deadlines. + +
+ +
+ + Student-first schedule flexibility +
+
+ + Portfolio building with university backing +
+
+ + Direct campus milestone payouts +
+
+
+ + {/* Card 2: Admins */} + + +
+ +
+ + For College Administrators + + + Hire talented students for labs, departmental projects, IT + support, and student union events with zero external vendor + friction. + +
+ +
+ + Instant student enrollment verification +
+
+ + Department budget tracking & approvals +
+
+ + Empower and retain on-campus talent +
+
+
+ + {/* Card 3: Security & Tech */} + + +
+ +
+ + Security & Reliability + + + Enterprise-grade safeguards ensuring student data privacy, + tamper-proof JWT session tokens, and encrypted credentials. + +
+ +
+ + HTTP-only cookie JWT architecture +
+
+ + Industry standard bcrypt password hashing +
+
+ + Strict university domain validation +
+
+
+
+
+
+ + {/* Minimalist Bottom CTA Banner using Shadcn Card */} +
+
+ +
+

+ Ready to join the campus network? +

+

+ Join university students, professors, and administrators on + CampusGigs today. +

+
+ +
+ {user ? ( + + ) : ( + <> + + + + )} +
+
+
+
+
+ ); } diff --git a/src/routes/login.tsx b/src/routes/login.tsx new file mode 100644 index 0000000..da2c88e --- /dev/null +++ b/src/routes/login.tsx @@ -0,0 +1,258 @@ +import { + createFileRoute, + Link, + redirect, + useRouter, +} from "@tanstack/react-router"; +import { + AlertCircle, + ArrowRight, + Briefcase, + Clock, + Eye, + EyeOff, + Loader2, + Lock, + Mail, +} from "lucide-react"; +import { useState } from "react"; +import { Button } from "../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "../components/ui/card"; +import { Input } from "../components/ui/input"; +import { Label } from "../components/ui/label"; +import { getCurrentUserFn, logInFn } from "../server/authFns"; + +export const Route = createFileRoute("/login")({ + beforeLoad: async () => { + const user = await getCurrentUserFn(); + if (user) { + if (user.role === "SUPER_ADMIN") { + throw redirect({ + to: "/admin", + }); + } + throw redirect({ + to: "/dashboard", + }); + } + }, + component: LoginPage, +}); + +function LoginPage() { + const router = useRouter(); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [loading, setLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + + const isPendingReview = Boolean( + errorMessage?.toLowerCase().includes("pending review"), + ); + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setErrorMessage(null); + + if (!email.trim() || !password) { + setErrorMessage("Please enter both your campus email and password."); + return; + } + + try { + setLoading(true); + const authRes = await logInFn({ + data: { + email: email.trim(), + password, + }, + }); + + // Invalidate route cache to refresh auth state in root and redirect + await router.invalidate(); + if (authRes.user?.role === "SUPER_ADMIN") { + await router.navigate({ to: "/admin" }); + } else { + await router.navigate({ to: "/dashboard" }); + } + } catch (err: unknown) { + const message = + err instanceof Error + ? err.message + : "Invalid credentials. Please verify your email and password."; + setErrorMessage(message); + } finally { + setLoading(false); + } + }; + + return ( +
+
+ {/* Top Branding */} +
+ +
+ +
+ + CampusGigs + + +
+ + {/* Shadcn Card for Login */} + + + + Welcome back + + + Enter your campus credentials to sign in to your account + + + + + {errorMessage && + (isPendingReview ? ( +
+
+ + Application Under Review +
+

+ {errorMessage} +

+
+ ) : ( +
+ +
+ {errorMessage} +
+
+ ))} + +
+
+ +
+
+ +
+ setEmail(e.target.value)} + placeholder="student@university.edu" + className="pl-9 h-10 bg-background" + /> +
+
+ +
+
+ + + Forgot password? + +
+
+
+ +
+ setPassword(e.target.value)} + placeholder="••••••••" + className="pl-9 pr-10 h-10 bg-background" + /> + +
+
+ + +
+
+ + +

+ Don't have an account?{" "} + + Create an account + +

+
+
+
+
+ ); +} diff --git a/src/routes/signup.tsx b/src/routes/signup.tsx new file mode 100644 index 0000000..8fba157 --- /dev/null +++ b/src/routes/signup.tsx @@ -0,0 +1,660 @@ +import { + createFileRoute, + Link, + redirect, + useRouter, +} from "@tanstack/react-router"; +import { + AlertCircle, + ArrowRight, + Briefcase, + Building2, + CheckCircle2, + Clock, + Eye, + EyeOff, + GraduationCap, + Loader2, + Lock, + Mail, + School, + User as UserIcon, +} from "lucide-react"; +import { useState } from "react"; +import { Badge } from "../components/ui/badge"; +import { Button } from "../components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "../components/ui/card"; +import { Input } from "../components/ui/input"; +import { Label } from "../components/ui/label"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "../components/ui/select"; +import { getCollegesFn, getCurrentUserFn, signUpFn } from "../server/authFns"; + +export const Route = createFileRoute("/signup")({ + beforeLoad: async () => { + const user = await getCurrentUserFn(); + if (user) { + throw redirect({ + to: "/dashboard", + }); + } + }, + loader: async () => { + const colleges = await getCollegesFn(); + return { colleges }; + }, + component: SignupPage, +}); + +function SignupPage() { + const router = useRouter(); + const { colleges } = Route.useLoaderData(); + + const [name, setName] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [role, setRole] = useState<"STUDENT" | "COLLEGE_ADMIN">("STUDENT"); + const [collegeName, setCollegeName] = useState(""); + const [showPassword, setShowPassword] = useState(false); + const [loading, setLoading] = useState(false); + const [errorMessage, setErrorMessage] = useState(null); + const [applicationSubmitted, setApplicationSubmitted] = useState(false); + const [submittedCollegeName, setSubmittedCollegeName] = useState(""); + const [submittedRole, setSubmittedRole] = useState< + "STUDENT" | "COLLEGE_ADMIN" + >("STUDENT"); + + const handleRoleChange = (newRole: "STUDENT" | "COLLEGE_ADMIN") => { + setRole(newRole); + setCollegeName(""); + setErrorMessage(null); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + setErrorMessage(null); + + if (role === "STUDENT" && colleges.length === 0) { + setErrorMessage( + "No active university portals yet. College administrators must apply and be approved by the Super Admin before students can register.", + ); + return; + } + + if (!name.trim() || !email.trim() || !password || !collegeName.trim()) { + setErrorMessage( + role === "STUDENT" + ? "Please fill in all fields and select your university." + : "Please fill in all required fields.", + ); + return; + } + + if (role === "STUDENT" && !colleges.includes(collegeName.trim())) { + setErrorMessage( + "Please select an approved participating university from the list.", + ); + return; + } + + if (password.length < 6) { + setErrorMessage("Password must be at least 6 characters long."); + return; + } + + try { + setLoading(true); + const res = await signUpFn({ + data: { + name: name.trim(), + email: email.trim(), + password, + role, + collegeName: collegeName.trim(), + }, + }); + + if (res.pendingApproval) { + setSubmittedRole(role); + setSubmittedCollegeName(collegeName.trim()); + setApplicationSubmitted(true); + return; + } + + // Invalidate route cache to refresh auth state in root and redirect to dashboard + await router.invalidate(); + await router.navigate({ to: "/dashboard" }); + } catch (err: unknown) { + const message = + err instanceof Error + ? err.message + : "Registration failed. Please check your details and try again."; + setErrorMessage(message); + } finally { + setLoading(false); + } + }; + + if (applicationSubmitted) { + const targetCollege = submittedCollegeName || collegeName; + const isStudentApp = submittedRole === "STUDENT"; + + return ( +
+
+
+ +
+ +
+ + CampusGigs + + +
+ + {isStudentApp ? ( + + +
+ +
+ + Student Application Submitted for Verification! + + + Your application to join the {targetCollege} portal has been + submitted to your campus administrator. Once accepted, you + will be able to log in to access your student workspace. + +
+ +
+
+ + Requested Campus + + + + {targetCollege} + +
+
+ + Applicant Role + + + + Student Freelancer + +
+
+ + Review Status + + + + Awaiting College Admin Approval + +
+
+ +
+ + +
+
+
+ ) : ( + + +
+ +
+ + Application Submitted for Review! + + + Your application to create the {targetCollege} portal has been + submitted to the Super Admin. Once accepted, you will be able + to log in to manage your campus. + +
+ +
+
+ + Requested Campus + + + + {targetCollege} + +
+
+ + Review Status + + + + Awaiting Super Admin Approval + +
+
+ +
+ + +
+
+
+ )} +
+
+ ); + } + + const isStudentWithoutColleges = role === "STUDENT" && colleges.length === 0; + + return ( +
+
+ {/* Top Branding */} +
+ +
+ +
+ + CampusGigs + + +
+ + {/* Shadcn Card for Signup */} + + +
+ + University Marketplace + +
+ + Create your account + + + Join your university network to find gigs or hire campus talent + +
+ + + {errorMessage && ( +
+ +
+ {errorMessage} +
+
+ )} + +
+ {/* Role Selector with Interactive Toggle and Accent Highlight */} +
+ +
+ + + +
+
+ + {/* Full Name */} +
+ +
+
+ +
+ setName(e.target.value)} + placeholder="Alex Johnson" + className="pl-9 h-10 bg-background" + /> +
+
+ + {/* Campus Email */} +
+ +
+
+ +
+ setEmail(e.target.value)} + placeholder="alex@university.edu" + className="pl-9 h-10 bg-background" + /> +
+
+ + {/* College or University Field */} +
+ + + {role === "STUDENT" ? ( + <> + {colleges.length === 0 && ( +
+ +
+ No active university portals yet. College + administrators must apply and be approved by the Super + Admin before students can register. +
+
+ )} + + +

+ {colleges.length === 0 + ? "Registration will open as soon as your campus portal is approved." + : "Only students from participating universities can join."} +

+ + ) : ( + <> +
+
+ +
+ { + setCollegeName(e.target.value); + setErrorMessage(null); + }} + placeholder="e.g. Stanford University or your campus" + className="pl-9 h-10 bg-background" + /> +
+

+ Apply to establish your university on CampusGigs. Requires + Super Admin approval before going live. +

+ + )} +
+ + {/* Password */} +
+ +
+
+ +
+ setPassword(e.target.value)} + placeholder="••••••••" + className="pl-9 pr-10 h-10 bg-background" + /> + +
+
+ +
+ + {isStudentWithoutColleges && ( +

+ Awaiting campus portal approval +

+ )} +
+
+ +
+ Already have an account?{" "} + + Sign in + +
+
+
+
+
+ ); +} diff --git a/src/server/adminFns.ts b/src/server/adminFns.ts new file mode 100644 index 0000000..df24172 --- /dev/null +++ b/src/server/adminFns.ts @@ -0,0 +1,154 @@ +import { createServerFn } from "@tanstack/react-start"; +import connectDB from "../lib/db.ts"; +import College, { type CollegeStatus } from "../models/College.ts"; +import User from "../models/User.ts"; +import { type AuthUser, getAuthCookie, verifyAccessToken } from "./auth.ts"; + +export interface CollegeApplicationItem { + _id: string; + name: string; + adminId?: string; + adminName?: string; + adminEmail?: string; + status: CollegeStatus; + appliedAt?: string; + reviewedAt?: string; + rejectionReason?: string; + createdAt?: string; + updatedAt?: string; +} + +export interface ReviewApplicationInput { + collegeId: string; + action: "ACCEPT" | "REJECT"; + reason?: string; +} + +export interface ReviewApplicationResponse { + success: true; + message: string; +} + +async function requireSuperAdmin(): Promise { + const token = getAuthCookie(); + if (!token) { + throw new Error("Forbidden: Super Admin only"); + } + + const user = await verifyAccessToken(token); + if (!user || user.role !== "SUPER_ADMIN") { + throw new Error("Forbidden: Super Admin only"); + } + + return user; +} + +/** + * Server function to fetch all college applications for Super Admin review. + * Validates caller has role === 'SUPER_ADMIN' (from token cookie). If not, throw Error('Forbidden: Super Admin only'). + * Returns all College documents sorted by appliedAt descending. + */ +export const getCollegeApplicationsFn = createServerFn({ + method: "GET", +}).handler(async (): Promise => { + await connectDB(); + await requireSuperAdmin(); + + const colleges = await College.find({}).sort({ appliedAt: -1 }).lean(); + + return colleges.map((c) => ({ + _id: c._id.toString(), + name: c.name, + adminId: c.adminId ? c.adminId.toString() : undefined, + adminName: c.adminName || "", + adminEmail: c.adminEmail || "", + status: (c.status as CollegeStatus) || "PENDING", + appliedAt: c.appliedAt ? new Date(c.appliedAt).toISOString() : undefined, + reviewedAt: c.reviewedAt ? new Date(c.reviewedAt).toISOString() : undefined, + rejectionReason: c.rejectionReason, + createdAt: c.createdAt ? new Date(c.createdAt).toISOString() : undefined, + updatedAt: c.updatedAt ? new Date(c.updatedAt).toISOString() : undefined, + })); +}); + +/** + * Server function for Super Admin to review a college portal application. + * Validates caller is 'SUPER_ADMIN'. + * If 'ACCEPT': updates College status to 'ACTIVE', reviewedAt to new Date(), and updates the corresponding User status to 'ACTIVE'. + * If 'REJECT': updates College status to 'REJECTED', reviewedAt to new Date(), rejectionReason to reason, and User status to 'REJECTED'. + * Returns { success: true, message: action === 'ACCEPT' ? 'College portal approved and activated.' : 'College portal application rejected.' } + */ +export const reviewCollegeApplicationFn = createServerFn({ method: "POST" }) + .validator( + (data: { + collegeId: string; + action: "ACCEPT" | "REJECT"; + reason?: string; + }) => data, + ) + .handler(async ({ data }): Promise => { + await connectDB(); + await requireSuperAdmin(); + + const { collegeId, action, reason } = data; + + if (!collegeId) { + throw new Error("College ID is required"); + } + + if (action !== "ACCEPT" && action !== "REJECT") { + throw new Error("Invalid review action. Must be ACCEPT or REJECT."); + } + + const college = await College.findById(collegeId); + if (!college) { + throw new Error("College application not found"); + } + + const reviewedAt = new Date(); + + if (action === "ACCEPT") { + college.status = "ACTIVE"; + college.reviewedAt = reviewedAt; + college.rejectionReason = undefined; + await college.save(); + + // Update corresponding User (by adminEmail or adminId) status to 'ACTIVE' + if (college.adminId) { + await User.findByIdAndUpdate(college.adminId, { status: "ACTIVE" }); + } + if (college.adminEmail) { + await User.findOneAndUpdate( + { email: college.adminEmail.toLowerCase().trim() }, + { status: "ACTIVE" }, + ); + } + + return { + success: true, + message: "College portal approved and activated.", + }; + } + + // action === 'REJECT' + college.status = "REJECTED"; + college.reviewedAt = reviewedAt; + college.rejectionReason = reason || "Application rejected by Super Admin"; + await college.save(); + + // Update corresponding User (by adminEmail or adminId) status to 'REJECTED' + if (college.adminId) { + await User.findByIdAndUpdate(college.adminId, { status: "REJECTED" }); + } + if (college.adminEmail) { + await User.findOneAndUpdate( + { email: college.adminEmail.toLowerCase().trim() }, + { status: "REJECTED" }, + ); + } + + return { + success: true, + message: "College portal application rejected.", + }; + }); diff --git a/src/server/auth.ts b/src/server/auth.ts new file mode 100644 index 0000000..4054723 --- /dev/null +++ b/src/server/auth.ts @@ -0,0 +1,89 @@ +import { + deleteCookie, + getCookie, + setCookie, +} from "@tanstack/react-start/server"; +import { type JWTPayload, jwtVerify, SignJWT } from "jose"; +import type { UserRole } from "../models/User.ts"; + +export const AUTH_COOKIE_NAME = "access_token"; +export const SEVEN_DAYS_IN_SECONDS = 7 * 24 * 60 * 60; // 7 days in seconds + +export interface AuthUser { + id: string; + name: string; + email: string; + role: UserRole; + collegeName: string; +} + +export type AuthPayload = AuthUser & JWTPayload; + +const JWT_SECRET_STRING = + process.env.JWT_SECRET || "campusgigs-super-secret-jwt-key-2026"; + +function getJwtSecretKey(): Uint8Array { + return new TextEncoder().encode(JWT_SECRET_STRING); +} + +/** + * Signs a JWT access token with 7 days expiration and HS256 algorithm. + */ +export async function signAccessToken( + payload: AuthUser | JWTPayload | Record, +): Promise { + return new SignJWT(payload as JWTPayload) + .setProtectedHeader({ alg: "HS256" }) + .setIssuedAt() + .setExpirationTime("7d") + .sign(getJwtSecretKey()); +} + +/** + * Verifies a JWT access token and returns the decoded payload, or null if invalid/expired. + */ +export async function verifyAccessToken( + token: string, +): Promise { + if (!token) { + return null; + } + + try { + const { payload } = await jwtVerify(token, getJwtSecretKey(), { + algorithms: ["HS256"], + }); + return payload as unknown as T; + } catch { + return null; + } +} + +/** + * Sets the httpOnly 'access_token' cookie with path '/', sameSite 'lax', maxAge 7 days, and secure in production. + */ +export function setAuthCookie(token: string): void { + setCookie(AUTH_COOKIE_NAME, token, { + httpOnly: true, + path: "/", + sameSite: "lax", + maxAge: SEVEN_DAYS_IN_SECONDS, + secure: process.env.NODE_ENV === "production", + }); +} + +/** + * Clears the 'access_token' cookie using deleteCookie from @tanstack/react-start/server. + */ +export function clearAuthCookie(): void { + deleteCookie(AUTH_COOKIE_NAME, { + path: "/", + }); +} + +/** + * Reads the 'access_token' cookie value using getCookie from @tanstack/react-start/server. + */ +export function getAuthCookie(): string | undefined { + return getCookie(AUTH_COOKIE_NAME); +} diff --git a/src/server/authFns.ts b/src/server/authFns.ts new file mode 100644 index 0000000..30b980f --- /dev/null +++ b/src/server/authFns.ts @@ -0,0 +1,311 @@ +import { createMiddleware, createServerFn } from "@tanstack/react-start"; +import bcryptjs from "bcryptjs"; +import connectDB from "../lib/db.ts"; +import College from "../models/College.ts"; +import User, { type UserRole } from "../models/User.ts"; +import { + type AuthUser, + clearAuthCookie, + getAuthCookie, + setAuthCookie, + signAccessToken, + verifyAccessToken, +} from "./auth.ts"; + +export type { AuthUser, UserRole }; + +export interface SignUpInput { + name: string; + email: string; + password: string; + role?: "STUDENT" | "COLLEGE_ADMIN"; + collegeName: string; +} + +export interface LogInInput { + email: string; + password: string; +} + +export interface AuthResponse { + success: true; + user?: AuthUser; + pendingApproval?: boolean; + message?: string; +} + +function escapeRegex(str: string): string { + return str.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); +} + +/** + * Server function to query all approved active colleges. + * Returns only colleges with status === 'ACTIVE'. If none exist, returns []. + */ +export const getCollegesFn = createServerFn({ method: "GET" }).handler( + async (): Promise => { + await connectDB(); + + const activeColleges = await College.find({ status: "ACTIVE" }) + .select("name") + .lean(); + + const collegeNames = activeColleges + .map((c) => c.name?.trim()) + .filter((name): name is string => Boolean(name)); + + return Array.from(new Set(collegeNames)).sort((a, b) => a.localeCompare(b)); + }, +); + +/** + * Authentication middleware that verifies JWT from the cookie + * and passes the authenticated user in context.user. + * Throws an error if not authenticated. + */ +export const authMiddleware = createMiddleware({ type: "function" }).server( + async ({ next }) => { + const token = getAuthCookie(); + if (!token) { + throw new Error("Unauthorized: No token provided"); + } + + const user = await verifyAccessToken(token); + if (!user || !user.id) { + throw new Error("Unauthorized: Invalid or expired token"); + } + + return next({ + context: { + user: { + id: user.id, + name: user.name, + email: user.email, + role: user.role, + collegeName: user.collegeName, + }, + }, + }); + }, +); + +/** + * Server function for user registration. + */ +export const signUpFn = createServerFn({ method: "POST" }) + .validator( + (data: { + name: string; + email: string; + password: string; + role?: "STUDENT" | "COLLEGE_ADMIN"; + collegeName: string; + }) => data, + ) + .handler(async ({ data }): Promise => { + await connectDB(); + + const normalizedEmail = data.email.toLowerCase().trim(); + const existingUser = await User.findOne({ email: normalizedEmail }); + + if (existingUser) { + throw new Error("Email already registered"); + } + + const userRole: UserRole = data.role || "STUDENT"; + const rawCollegeName = data.collegeName ? data.collegeName.trim() : ""; + const hashedPassword = await bcryptjs.hash(data.password, 10); + + if (userRole === "COLLEGE_ADMIN") { + if (!rawCollegeName) { + throw new Error("College name is required"); + } + + const existingCollege = await College.findOne({ + name: { $regex: new RegExp(`^${escapeRegex(rawCollegeName)}$`, "i") }, + }); + + if (existingCollege) { + if (existingCollege.status === "ACTIVE") { + throw new Error( + "A college portal with this name already exists and is active.", + ); + } + if (existingCollege.status === "PENDING") { + throw new Error( + "An application for this college portal is already pending review.", + ); + } + } + + // Create College Admin user with PENDING_REVIEW status + const user = await User.create({ + name: data.name.trim(), + email: normalizedEmail, + password: hashedPassword, + role: "COLLEGE_ADMIN", + collegeName: rawCollegeName, + status: "PENDING_REVIEW", + }); + + // Create College application with PENDING status + await College.create({ + name: rawCollegeName, + adminId: user._id, + adminName: user.name, + adminEmail: user.email, + status: "PENDING", + appliedAt: new Date(), + }); + + // Do NOT set auth cookie for pending college admin + return { + success: true, + pendingApproval: true, + message: + "Your college portal application has been submitted for Super Admin review.", + }; + } + + // userRole === 'STUDENT' + if (!rawCollegeName) { + throw new Error("Please select an active, approved university portal."); + } + + const activeCollege = await College.findOne({ + name: { $regex: new RegExp(`^${escapeRegex(rawCollegeName)}$`, "i") }, + status: "ACTIVE", + }); + + if (!activeCollege) { + throw new Error("Please select an active, approved university portal."); + } + + await User.create({ + name: data.name.trim(), + email: normalizedEmail, + password: hashedPassword, + role: "STUDENT", + collegeName: activeCollege.name, + status: "PENDING_REVIEW", + }); + + return { + success: true, + pendingApproval: true, + message: + "Your student application to join " + + activeCollege.name + + " has been submitted to your college administrator for review.", + }; + }); + +/** + * Server function for user login. + */ +export const logInFn = createServerFn({ method: "POST" }) + .validator((data: { email: string; password: string }) => data) + .handler(async ({ data }): Promise => { + await connectDB(); + + const normalizedEmail = data.email.toLowerCase().trim(); + const user = await User.findOne({ email: normalizedEmail }); + + if (!user) { + throw new Error("Invalid email or password"); + } + + const isPasswordValid = await bcryptjs.compare( + data.password, + user.password, + ); + if (!isPasswordValid) { + throw new Error("Invalid email or password"); + } + + if (user.role === "STUDENT" && user.status === "PENDING_REVIEW") { + throw new Error( + "Your student application to join " + + user.collegeName + + " is currently pending review by your college administrator. You will be able to log in once approved.", + ); + } + + if (user.role === "STUDENT" && user.status === "REJECTED") { + throw new Error( + "Your student application to join " + + user.collegeName + + " was not approved by the college administrator.", + ); + } + + if (user.role === "COLLEGE_ADMIN" && user.status === "PENDING_REVIEW") { + throw new Error( + "Your college portal application is currently pending review by the Super Admin. You can log in once approved.", + ); + } + + if (user.status === "REJECTED") { + throw new Error( + "Your account or portal application was rejected. Please contact support.", + ); + } + + if (user.status === "SUSPENDED") { + throw new Error( + "Your account has been suspended. Please contact support.", + ); + } + + const authUser: AuthUser = { + id: user._id.toString(), + name: user.name, + email: user.email, + role: user.role as UserRole, + collegeName: user.collegeName, + }; + + const token = await signAccessToken(authUser); + setAuthCookie(token); + + return { + success: true, + user: authUser, + }; + }); + +/** + * Server function for user logout. + */ +export const logOutFn = createServerFn({ method: "POST" }).handler( + async (): Promise<{ success: true }> => { + clearAuthCookie(); + return { success: true }; + }, +); + +/** + * Server function to get current logged in user. + * Returns AuthUser or null if not logged in. + */ +export const getCurrentUserFn = createServerFn({ method: "GET" }).handler( + async (): Promise => { + const token = getAuthCookie(); + if (!token) { + return null; + } + + const payload = await verifyAccessToken(token); + if (!payload || !payload.id) { + return null; + } + + return { + id: payload.id, + name: payload.name, + email: payload.email, + role: payload.role, + collegeName: payload.collegeName, + }; + }, +); diff --git a/src/server/collegeAdminFns.ts b/src/server/collegeAdminFns.ts new file mode 100644 index 0000000..b7d3944 --- /dev/null +++ b/src/server/collegeAdminFns.ts @@ -0,0 +1,149 @@ +import { createServerFn } from "@tanstack/react-start"; +import connectDB from "../lib/db.ts"; +import User, { type UserStatus } from "../models/User.ts"; +import { authMiddleware } from "./authFns.ts"; + +export interface StudentApplicationItem { + id: string; + name: string; + email: string; + collegeName: string; + status: UserStatus; + createdAt?: Date; + reviewedAt?: Date; + rejectionReason?: string; +} + +export interface ReviewStudentApplicationInput { + studentId: string; + action: "APPROVE" | "REJECT"; + rejectionReason?: string; +} + +export interface ReviewStudentApplicationResponse { + success: true; + message: string; +} + +/** + * Server function to fetch student applications for college admin review. + * College admins can only view students from their own college. + * Super admins can view all students. + */ +export const getStudentApplicationsFn = createServerFn({ method: "GET" }) + .middleware([authMiddleware]) + .handler(async ({ context }): Promise => { + await connectDB(); + + const caller = context.user; + if (caller.role !== "COLLEGE_ADMIN" && caller.role !== "SUPER_ADMIN") { + throw new Error("Forbidden: College Admin access required"); + } + + const query: Record = { role: "STUDENT" }; + if (caller.role === "COLLEGE_ADMIN") { + query.collegeName = caller.collegeName; + } + + const students = await User.find(query).lean(); + + // Sorted with pending first, then by createdAt desc + students.sort((a, b) => { + const aIsPending = a.status === "PENDING_REVIEW" ? 0 : 1; + const bIsPending = b.status === "PENDING_REVIEW" ? 0 : 1; + + if (aIsPending !== bIsPending) { + return aIsPending - bIsPending; + } + + const aTime = a.createdAt ? new Date(a.createdAt).getTime() : 0; + const bTime = b.createdAt ? new Date(b.createdAt).getTime() : 0; + return bTime - aTime; + }); + + return students.map((student) => ({ + id: student._id.toString(), + name: student.name, + email: student.email, + collegeName: student.collegeName, + status: student.status, + createdAt: student.createdAt ? new Date(student.createdAt) : undefined, + reviewedAt: student.reviewedAt ? new Date(student.reviewedAt) : undefined, + rejectionReason: student.rejectionReason, + })); + }); + +/** + * Server function for College Admin (or Super Admin) to review student applications. + */ +export const reviewStudentApplicationFn = createServerFn({ method: "POST" }) + .middleware([authMiddleware]) + .validator( + (data: { + studentId: string; + action: "APPROVE" | "REJECT"; + rejectionReason?: string; + }) => data, + ) + .handler( + async ({ context, data }): Promise => { + await connectDB(); + + const caller = context.user; + if (caller.role !== "COLLEGE_ADMIN" && caller.role !== "SUPER_ADMIN") { + throw new Error("Forbidden: College Admin access required"); + } + + const { studentId, action, rejectionReason } = data; + + if (!studentId) { + throw new Error("Student ID is required"); + } + + if (action !== "APPROVE" && action !== "REJECT") { + throw new Error("Invalid review action. Must be APPROVE or REJECT."); + } + + const student = await User.findById(studentId); + if (!student) { + throw new Error("Student not found"); + } + + if ( + caller.role === "COLLEGE_ADMIN" && + student.collegeName !== caller.collegeName + ) { + throw new Error( + "Forbidden: You cannot review students from another college", + ); + } + + const reviewedAt = new Date(); + + if (action === "APPROVE") { + student.status = "ACTIVE"; + student.reviewedAt = reviewedAt; + student.reviewedBy = caller.id; + student.rejectionReason = undefined; + await student.save(); + + return { + success: true, + message: "Student approved successfully", + }; + } + + // action === 'REJECT' + student.status = "REJECTED"; + student.rejectionReason = + rejectionReason || "Application rejected by college administrator"; + student.reviewedAt = reviewedAt; + student.reviewedBy = caller.id; + await student.save(); + + return { + success: true, + message: "Student application rejected", + }; + }, + ); diff --git a/src/server/superAdmin.ts b/src/server/superAdmin.ts new file mode 100644 index 0000000..a48055c --- /dev/null +++ b/src/server/superAdmin.ts @@ -0,0 +1,77 @@ +import bcryptjs from "bcryptjs"; +import College from "../models/College.ts"; +import User from "../models/User.ts"; + +export const PRESEEDED_COLLEGES = [ + "Stanford University", + "Massachusetts Institute of Technology", + "UC Berkeley", + "Harvard University", + "Carnegie Mellon University", + "University of Washington", + "Columbia University", + "Georgia Institute of Technology", +]; + +/** + * Ensures that the Super Administrator account exists and has active status, + * and removes all legacy pre-seeded colleges from MongoDB Atlas. + */ +export async function ensureSuperAdmin(): Promise { + try { + // Clean up any legacy pre-seeded universities from database + await College.deleteMany({ name: { $in: PRESEEDED_COLLEGES } }); + + const email = (process.env.SUPER_ADMIN_EMAIL || "superadmin@campusgigs.edu") + .toLowerCase() + .trim(); + const password = process.env.SUPER_ADMIN_PASSWORD || "SuperAdminPass2026!"; + + const existing = await User.findOne({ email }); + + if (!existing) { + const hashedPassword = await bcryptjs.hash(password, 10); + await User.create({ + name: "Super Administrator", + email, + password: hashedPassword, + role: "SUPER_ADMIN", + status: "ACTIVE", + collegeName: "CampusGigs HQ", + }); + console.log(`[SuperAdmin] Super Administrator created: ${email}`); + } else { + let updated = false; + + if (existing.role !== "SUPER_ADMIN") { + existing.role = "SUPER_ADMIN"; + updated = true; + } + + if (existing.status !== "ACTIVE") { + existing.status = "ACTIVE"; + updated = true; + } + + if (existing.collegeName !== "CampusGigs HQ") { + existing.collegeName = "CampusGigs HQ"; + updated = true; + } + + const passwordMatch = await bcryptjs.compare(password, existing.password); + if (!passwordMatch) { + existing.password = await bcryptjs.hash(password, 10); + updated = true; + } + + if (updated) { + await existing.save(); + console.log(`[SuperAdmin] Super Administrator synchronized: ${email}`); + } + } + } catch (error) { + console.error("[SuperAdmin] Error ensuring Super Admin:", error); + } +} + +export default ensureSuperAdmin; diff --git a/src/styles.css b/src/styles.css index 3295f79..25df825 100644 --- a/src/styles.css +++ b/src/styles.css @@ -1,4 +1,5 @@ @import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,500;9..144,700&family=Manrope:wght@400;500;600;700;800&display=swap'); +@import './global.css'; @import 'tailwindcss'; @plugin '@tailwindcss/typography'; @@ -6,117 +7,6 @@ @custom-variant dark (&:is(.dark *)); -:root { - --sea-ink: #173a40; - --sea-ink-soft: #416166; - --lagoon: #4fb8b2; - --lagoon-deep: #328f97; - --palm: #2f6a4a; - --sand: #e7f0e8; - --foam: #f3faf5; - --surface: rgba(255, 255, 255, 0.74); - --surface-strong: rgba(255, 255, 255, 0.9); - --line: rgba(23, 58, 64, 0.14); - --inset-glint: rgba(255, 255, 255, 0.82); - --kicker: rgba(47, 106, 74, 0.9); - --bg-base: #e7f3ec; - --header-bg: rgba(251, 255, 248, 0.84); - --chip-bg: rgba(255, 255, 255, 0.8); - --chip-line: rgba(47, 106, 74, 0.18); - --link-bg-hover: rgba(255, 255, 255, 0.9); - --hero-a: rgba(79, 184, 178, 0.36); - --hero-b: rgba(47, 106, 74, 0.2); - - --background: oklch(1 0 0); - --foreground: oklch(0.141 0.005 285.823); - --card: oklch(1 0 0); - --card-foreground: oklch(0.141 0.005 285.823); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.141 0.005 285.823); - --primary: oklch(0.21 0.006 285.885); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.967 0.001 286.375); - --secondary-foreground: oklch(0.21 0.006 285.885); - --muted: oklch(0.967 0.001 286.375); - --muted-foreground: oklch(0.552 0.016 285.938); - --accent: oklch(0.967 0.001 286.375); - --accent-foreground: oklch(0.21 0.006 285.885); - --destructive: oklch(0.577 0.245 27.325); - --destructive-foreground: oklch(0.577 0.245 27.325); - --border: oklch(0.92 0.004 286.32); - --input: oklch(0.92 0.004 286.32); - --ring: oklch(0.871 0.006 286.286); - --chart-1: oklch(0.646 0.222 41.116); - --chart-2: oklch(0.6 0.118 184.704); - --chart-3: oklch(0.398 0.07 227.392); - --chart-4: oklch(0.828 0.189 84.429); - --chart-5: oklch(0.769 0.188 70.08); - --radius: 0.625rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.141 0.005 285.823); - --sidebar-primary: oklch(0.21 0.006 285.885); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.967 0.001 286.375); - --sidebar-accent-foreground: oklch(0.21 0.006 285.885); - --sidebar-border: oklch(0.92 0.004 286.32); - --sidebar-ring: oklch(0.871 0.006 286.286); -} - -.dark { - --sea-ink: #d7ece8; - --sea-ink-soft: #afcdc8; - --lagoon: #60d7cf; - --lagoon-deep: #8de5db; - --palm: #6ec89a; - --sand: #0f1a1e; - --foam: #101d22; - --surface: rgba(16, 30, 34, 0.8); - --surface-strong: rgba(15, 27, 31, 0.92); - --line: rgba(141, 229, 219, 0.18); - --inset-glint: rgba(194, 247, 238, 0.14); - --kicker: #b8efe5; - --bg-base: #0a1418; - --header-bg: rgba(10, 20, 24, 0.8); - --chip-bg: rgba(13, 28, 32, 0.9); - --chip-line: rgba(141, 229, 219, 0.24); - --link-bg-hover: rgba(24, 44, 49, 0.8); - --hero-a: rgba(96, 215, 207, 0.18); - --hero-b: rgba(110, 200, 154, 0.12); - - --background: oklch(0.141 0.005 285.823); - --foreground: oklch(0.985 0 0); - --card: oklch(0.141 0.005 285.823); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.141 0.005 285.823); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.985 0 0); - --primary-foreground: oklch(0.21 0.006 285.885); - --secondary: oklch(0.274 0.006 286.033); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.274 0.006 286.033); - --muted-foreground: oklch(0.705 0.015 286.067); - --accent: oklch(0.274 0.006 286.033); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.396 0.141 25.723); - --destructive-foreground: oklch(0.637 0.237 25.331); - --border: oklch(0.274 0.006 286.033); - --input: oklch(0.274 0.006 286.033); - --ring: oklch(0.442 0.017 285.786); - --chart-1: oklch(0.488 0.243 264.376); - --chart-2: oklch(0.696 0.17 162.48); - --chart-3: oklch(0.769 0.188 70.08); - --chart-4: oklch(0.627 0.265 303.9); - --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.21 0.006 285.885); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.274 0.006 286.033); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(0.274 0.006 286.033); - --sidebar-ring: oklch(0.442 0.017 285.786); -} - @theme inline { --font-sans: 'Manrope', ui-sans-serif, system-ui, sans-serif; --color-background: var(--background); @@ -165,177 +55,14 @@ body, body { margin: 0; - color: var(--sea-ink); + color: var(--foreground); font-family: var(--font-sans); - background-color: var(--bg-base); - background: - radial-gradient(1100px 620px at -8% -10%, var(--hero-a), transparent 58%), - radial-gradient(1050px 620px at 112% -12%, var(--hero-b), transparent 62%), - radial-gradient(720px 380px at 50% 115%, rgba(79, 184, 178, 0.1), transparent 68%), - linear-gradient(180deg, color-mix(in oklab, var(--sand) 68%, white) 0%, var(--foam) 44%, var(--bg-base) 100%); + background-color: var(--background); overflow-x: hidden; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } -body::before { - content: ''; - position: fixed; - inset: 0; - pointer-events: none; - z-index: -1; - opacity: 0.28; - background: - radial-gradient(circle at 20% 15%, rgba(255, 255, 255, 0.8), transparent 34%), - radial-gradient(circle at 78% 26%, rgba(79, 184, 178, 0.2), transparent 42%), - radial-gradient(circle at 42% 82%, rgba(47, 106, 74, 0.14), transparent 36%); -} - -body::after { - content: ''; - position: fixed; - inset: 0; - pointer-events: none; - z-index: -1; - opacity: 0.14; - background-image: - linear-gradient(rgba(255, 255, 255, 0.07) 1px, transparent 1px), - linear-gradient(90deg, rgba(255, 255, 255, 0.06) 1px, transparent 1px); - background-size: 28px 28px; - mask-image: radial-gradient(circle at 50% 30%, black, transparent 78%); -} - -a { - color: var(--lagoon-deep); - text-decoration-color: rgba(50, 143, 151, 0.4); - text-decoration-thickness: 1px; - text-underline-offset: 2px; -} - -a:hover { - color: #246f76; -} - -code { - font-size: 0.9em; - border: 1px solid var(--line); - background: color-mix(in oklab, var(--surface-strong) 82%, white 18%); - border-radius: 7px; - padding: 2px 7px; -} - -pre code { - border: 0; - background: transparent; - padding: 0; - border-radius: 0; - font-size: inherit; - color: inherit; -} - -.prose pre { - border: 1px solid var(--line); - border-radius: 12px; - background: #1d2e45; - color: #e8efff; -} - -.page-wrap { - width: min(1080px, calc(100% - 2rem)); - margin-inline: auto; -} - -.display-title { - font-family: 'Fraunces', Georgia, serif; -} - -.island-shell { - border: 1px solid var(--line); - background: linear-gradient(165deg, var(--surface-strong), var(--surface)); - box-shadow: - 0 1px 0 var(--inset-glint) inset, - 0 22px 44px rgba(30, 90, 72, 0.1), - 0 6px 18px rgba(23, 58, 64, 0.08); - backdrop-filter: blur(4px); -} - -.feature-card { - background: linear-gradient(165deg, color-mix(in oklab, var(--surface-strong) 93%, white 7%), var(--surface)); - box-shadow: - 0 1px 0 var(--inset-glint) inset, - 0 18px 34px rgba(30, 90, 72, 0.1), - 0 4px 14px rgba(23, 58, 64, 0.06); -} - -.feature-card:hover { - transform: translateY(-2px); - border-color: color-mix(in oklab, var(--lagoon-deep) 35%, var(--line)); -} - -button, -.island-shell, -a { - transition: background-color 180ms ease, color 180ms ease, border-color 180ms ease, - transform 180ms ease; -} - -.island-kicker { - letter-spacing: 0.16em; - text-transform: uppercase; - font-weight: 700; - font-size: 0.69rem; - color: var(--kicker); -} - -.nav-link { - position: relative; - text-decoration: none; - color: var(--sea-ink-soft); -} - -.nav-link::after { - content: ''; - position: absolute; - left: 0; - bottom: -8px; - width: 100%; - height: 2px; - transform: scaleX(0); - transform-origin: left; - background: linear-gradient(90deg, var(--lagoon), #7ed3bf); - transition: transform 170ms ease; -} - -.nav-link:hover, -.nav-link.is-active { - color: var(--sea-ink); -} - -.nav-link:hover::after, -.nav-link.is-active::after { - transform: scaleX(1); -} - -.rise-in { - animation: rise-in 700ms cubic-bezier(0.16, 1, 0.3, 1) both; -} - -@keyframes rise-in { - from { - opacity: 0; - transform: translateY(12px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -.site-footer { - border-top: 1px solid var(--line); - background: color-mix(in oklab, var(--header-bg) 84%, transparent 16%); -} - @layer base { * { @apply border-border outline-ring/50; diff --git a/vite.config.ts b/vite.config.ts index f5d9604..e711393 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,15 +1,15 @@ -import { defineConfig } from 'vite' -import { devtools } from '@tanstack/devtools-vite' +import tailwindcss from "@tailwindcss/vite"; +import { devtools } from "@tanstack/devtools-vite"; -import { tanstackStart } from '@tanstack/react-start/plugin/vite' +import { tanstackStart } from "@tanstack/react-start/plugin/vite"; -import viteReact from '@vitejs/plugin-react' -import tailwindcss from '@tailwindcss/vite' -import { nitro } from 'nitro/vite' +import viteReact from "@vitejs/plugin-react"; +import { nitro } from "nitro/vite"; +import { defineConfig } from "vite"; const config = defineConfig({ - resolve: { tsconfigPaths: true }, - plugins: [devtools(), nitro(), tailwindcss(), tanstackStart(), viteReact()], -}) + resolve: { tsconfigPaths: true }, + plugins: [devtools(), nitro(), tailwindcss(), tanstackStart(), viteReact()], +}); -export default config +export default config;