diff --git a/.gitignore b/.gitignore index 54d5ecf..64b2a6d 100644 --- a/.gitignore +++ b/.gitignore @@ -13,9 +13,11 @@ package-lock.json # testing /coverage +/backup/ # next.js /.next/ +/.next-dev/ /out/ # production diff --git a/AGENTS.md b/AGENTS.md deleted file mode 100644 index ef23239..0000000 --- a/AGENTS.md +++ /dev/null @@ -1,143 +0,0 @@ -# AGENTS.md — Guidelines for AI Coding Agents - -## Project Overview - -CSSApply is a Next.js 15 recruitment portal for the Computer Science Society at UST. It uses TypeScript, Tailwind CSS v4, Prisma ORM (PostgreSQL), NextAuth.js, Supabase for storage, SWR for client caching, and sonner for toast notifications. - -## Build / Lint / Test Commands - -```bash -npm run dev # Dev server (Turbopack) -npm run build # Production build -npm run start # Production server -npm run lint # ESLint (flat config) -npm run lint:fix # ESLint with auto-fix -npx tsc --noEmit # TypeScript type check (no dedicated script) -``` - -```bash -npx prisma generate # Regenerate Prisma client after schema changes -npx prisma db push # Push schema to database -npx prisma studio # Open Prisma Studio GUI -``` - -**Testing**: No test framework configured. `src/test/ApplicationGuard.test.tsx` is a demo file. Recommend Vitest or Jest if adding tests. - -**Prisma note**: `@prisma/client` (^6) and `prisma` CLI (^6) versions must match. If `prisma generate` fails with EPERM, stop the dev server first. - -## Project Structure - -``` -src/ -├── app/ # Next.js App Router -│ ├── api/ # Route handlers (route.ts) -│ ├── admin/ # Admin dashboard (schedule, applications, members, EAs, staffs, super-admin) -│ ├── user/ # User-facing pages (apply, member/committee-staff/executive-assistant flows) -│ └── auth/ # Auth error pages -├── components/ # Shared React components (PascalCase) -├── contexts/ # React context providers -├── data/ # Static data (committeeRoles, ebRoles, adminSchedule) -├── lib/ # Utilities (auth, prisma, supabase, email, SWR helpers) -├── styles/ # Animation utilities -├── types/ # TypeScript augmentations -└── middleware.ts # Auth/role routing -prisma/schema.prisma # Database schema -eslint.config.mjs # ESLint flat config -``` - -## Code Style - -### TypeScript - -- `strict: true`. No `any` unless unavoidable. -- Use `interface` for props and object shapes. PascalCase names (`UserSession`, `ApplicationGuardProps`). -- Import types with `import type { ... }`. -- Path alias: `@/*` → `./src/*`. Never use relative `../../`. - -### React Components - -- Client components: `"use client"` at top. Server components have no directive. -- Default exports for pages and shared components. -- Props inline or separate `interface` above the function. - -```tsx -"use client"; -import { useState } from "react"; - -interface Props { - title: string; - optional?: boolean; -} - -export default function MyComponent({ title, optional = false }: Props) { - // ... -} -``` - -### Naming - -- **Files**: `kebab-case` for utils (`name-parsing.ts`), `PascalCase` for components (`ApplicationGuard.tsx`). -- **Components/functions**: PascalCase / camelCase. -- **Constants**: UPPER_SNAKE_CASE. -- **Routes**: Next.js conventions (`page.tsx`, `route.ts`, `[param]/`). - -### API Routes - -- Export named `GET`, `POST`, etc. handlers. -- Always check `getServerSession(authOptions)` first. -- Return `NextResponse.json(data, { status })`. -- Wrap in try/catch. Use `console.error('Context:', err)`. - -### Error Handling - -- Server: try/catch → `{ error: 'message' }` with 400/401/404/500. -- Client: try/catch around fetches, use `toast` from `sonner` for user feedback. -- Never use `alert()` — use `toast.success()` / `toast.error()`. - -### Imports - -- Order: (1) React/Next, (2) third-party, (3) internal `@/`. -- Named imports preferred: `import { createClient } from '@supabase/supabase-js'`. - -### Styling - -- Tailwind CSS v4 via `@tailwindcss/postcss`. Utility classes only. -- Theme palette: `#044FAF` (primary blue), `#134687` (dark blue), `#005FD9` (accent), `#F3F3FD` (bg), `#E8F2FF` (light blue). -- Fonts: `font-poppins` (headings), `font-inter` (body), `font-mono` (labels/code). -- Card style: `bg-white rounded-xl border border-[#005FD9]/10 p-5`. -- Buttons: flat outline `text-[#134687] border border-[#005FD9]/15 rounded hover:bg-[#F3F3FD]`. -- Status badges: `bg-[#044FAF]/10 text-[#044FAF]` (positive), `bg-[#FFE7B4]/40 text-[#5B4515]` (pending). -- Toast: sonner with custom CSS in `globals.css` (blue success, soft red error). - -### Data Fetching - -- Client: use SWR (`import useSWR from 'swr'`) with the fetcher in `src/lib/swr-fetcher.ts`. -- Session: wrap app in `SessionWrapper` which provides `SessionProvider` + `SWRConfig`. -- Avoid redundant fetches — SWR deduplicates automatically. - -### Database (Prisma) - -- Schema: `prisma/schema.prisma`. PostgreSQL. -- Singleton client: `src/lib/prisma.ts`. -- After schema changes: `npx prisma generate && npx prisma db push`. -- Use `select`/`include` to control returned fields. -- DB indexes on frequently filtered columns. - -## Environment Variables - -``` -DATABASE_URL, NEXTAUTH_URL, NEXTAUTH_SECRET, -GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, -NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_ANON_KEY, SUPABASE_SERVICE_ROLE_KEY, -BREVO_API_KEY -``` - -Never commit `.env`. Never log secrets. - -## Middleware - -`src/middleware.ts`: redirects authenticated users to role dashboards, protects `/user/*` and `/admin/*`, enforces super-admin access. - -## Deployment - -Vercel. Build command: `next build`. `vercel.json` present for config. diff --git a/README.md b/README.md index 164ad26..d2c62db 100644 --- a/README.md +++ b/README.md @@ -8,8 +8,8 @@ [![Next.js](https://img.shields.io/badge/Next.js-15.4.6-black?style=for-the-badge&logo=next.js)](https://nextjs.org/) [![TypeScript](https://img.shields.io/badge/TypeScript-5.0-blue?style=for-the-badge&logo=typescript)](https://www.typescriptlang.org/) -[![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-4.1.12-38B2AC?style=for-the-badge&logo=tailwind-css)](https://tailwindcss.com/) -[![Prisma](https://img.shields.io/badge/Prisma-6.16.1-2D3748?style=for-the-badge&logo=prisma)](https://prisma.io/) +[![Tailwind CSS](https://img.shields.io/badge/Tailwind%20CSS-4.2.2-38B2AC?style=for-the-badge&logo=tailwind-css)](https://tailwindcss.com/) +[![Prisma](https://img.shields.io/badge/Prisma-7.7.0-2D3748?style=for-the-badge&logo=prisma)](https://prisma.io/) @@ -24,10 +24,11 @@ CSSApply is a comprehensive recruitment management system designed specifically - **Multi-Position Applications**: Support for Members, Committee Staff, and Executive Assistant positions - **Smart Interview Scheduling**: Automated conflict detection and prevention - **Admin Dashboard**: Comprehensive management tools for recruitment staff -- **Email Notifications**: Automated communication system using Brevo +- **Email Notifications**: Automated communication system using Brevo with test email feature - **Personality Assessment**: Integrated personality test for candidate evaluation - **Secure Authentication**: NextAuth.js integration with role-based access control - **Responsive Design**: Mobile-first approach with modern UI/UX +- **Performance Optimized**: SWR caching for fast data fetching and instant page loads --- @@ -79,6 +80,10 @@ CSSApply is a comprehensive recruitment management system designed specifically NEXT_PUBLIC_SUPABASE_URL="your-supabase-url" NEXT_PUBLIC_SUPABASE_ANON_KEY="your-supabase-anon-key" SUPABASE_SERVICE_ROLE_KEY="your-service-role-key" + NEXT_PUBLIC_PAYMENT_QR_URL="https://your-public-payment-qr-url" + + # Auth + ALLOWED_SIGNIN_EMAIL_DOMAIN="ust.edu.ph" # Email (Brevo) BREVO_API_KEY="your-brevo-api-key" @@ -137,6 +142,10 @@ css-apply/ | `npm run build` | Build the application for production | | `npm run start` | Start the production server | | `npm run lint` | Run ESLint for code quality | +| `npm run lint:fix` | Run ESLint with auto-fix | +| `npx prisma generate` | Regenerate Prisma client | +| `npx prisma db push` | Push schema changes to database | +| `npx prisma studio` | Open Prisma Studio GUI | --- @@ -162,6 +171,26 @@ css-apply/ --- +## Admin Features + +### Super Admin Dashboard + +The Super Admin dashboard provides comprehensive system management: + +- **User Database (user_db)**: View, search, and manage all users. Assign EB (Executive Board) profiles, change roles (user/admin/super_admin), and view member statistics. +- **Configuration (config)**: Manage recruitment cycles with start/end dates and active status. +- **Test Email (email_test)**: Send test emails for all email templates to verify delivery and design. + +### EB Profile Management + +Super Admins can assign and manage Executive Board profiles including: +- Position (President, Secretary, Treasurer, etc.) +- Committees assigned +- Meeting links for interviews +- Active/inactive status + +--- + ## Security Features - **Authentication**: NextAuth.js with multiple providers @@ -181,11 +210,13 @@ CSSApply uses **Brevo** (formerly Sendinblue) for email communications: - **Application Confirmations**: Successful submissions - **Interview Notifications**: Schedule confirmations - **Admin Alerts**: System notifications +- **Test Emails**: Super Admins can send test emails to verify templates ### Email Templates - Responsive HTML templates -- Brand-consistent styling +- Brand-consistent styling with blue theme colors +- Clean design with logo instead of text - Multi-language support ready --- @@ -209,6 +240,31 @@ CSSApply uses **Brevo** (formerly Sendinblue) for email communications: --- +## Performance Optimization + +CSSApply uses several techniques to ensure fast performance: + +### SWR Caching + +- Client-side data caching with SWR (Stale-While-Revalidate) +- EB profiles and schedule data are cached for 5 minutes +- Eliminates loading spinners on return visits +- Automatic revalidation disabled to reduce server load + +### Loading States + +- All admin and user pages show loading spinners during data fetches +- Prevents "flash" of empty content when loading +- Consistent UX across all pages + +### Database Optimization + +- Prisma queries optimized with proper `select` statements +- Pagination implemented for large datasets +- Indexes on frequently queried columns + +--- + ## Deployment ### Vercel (Recommended) diff --git a/docs/BREVO_SETUP.md b/docs/BREVO_SETUP.md index 1f63596..77024e9 100644 --- a/docs/BREVO_SETUP.md +++ b/docs/BREVO_SETUP.md @@ -1,6 +1,6 @@ # Brevo Email Integration Setup -This document explains how to set up Brevo email integration for the CSS Apply application. +This document explains how to set up Brevo email integration for the CSSApply application. ## Prerequisites @@ -54,7 +54,7 @@ When users submit applications, they automatically receive confirmation emails f Each application type has a customized email template that includes: -- Professional CSS Apply branding +- Professional CSSApply branding - Application details (student number, application type, choices) - Status information - Contact information for questions diff --git a/docs/CSS_REMINDER_README.md b/docs/CSS_REMINDER_README.md index dfadf61..edef07a 100644 --- a/docs/CSS_REMINDER_README.md +++ b/docs/CSS_REMINDER_README.md @@ -59,7 +59,7 @@ node send-css-group-reminder.js The email includes: -- Professional CSS Apply branding +- Professional CSSApply branding - Personalized greeting with user's name - Clear call-to-action to join the CSS Group - Facebook group link: `https://fb.me/g/6WRg4o62h/xpTx6zKB` diff --git a/eslint.config.mjs b/eslint.config.mjs index 9d16d61..ef501cc 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -14,6 +14,7 @@ const eslintConfig = [ ignores: [ "node_modules/**", ".next/**", + ".next-dev/**", "out/**", "build/**", "src/generated/**", diff --git a/next.config.ts b/next.config.ts index 62d6200..4179495 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,9 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { + // Keep development hot-reload artifacts separate from production builds. + // Running `next build` must not invalidate an active `next dev` server. + distDir: process.env.NODE_ENV === "development" ? ".next-dev" : ".next", poweredByHeader: false, experimental: { serverActions: { diff --git a/package-lock.json b/package-lock.json index 56b6227..dae869c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,24 +16,24 @@ "@hookform/resolvers": "^5.2.2", "@iconify/react": "^6.0.2", "@next-auth/prisma-adapter": "^1.0.7", - "@prisma/adapter-pg": "^7.7.0", - "@prisma/client": "^7.7.0", + "@prisma/adapter-pg": "^7.9.1", + "@prisma/client": "^7.9.1", "@supabase/ssr": "^0.10.0", "@supabase/supabase-js": "^2.102.1", "formidable": "^3.5.4", "gsap": "^3.14.2", "lucide-react": "^0.544.0", - "next": "^15.4.6", - "next-auth": "^4.24.13", + "next": "^15.5.23", + "next-auth": "^4.24.15", "pg": "^8.20.0", - "postcss": "^8.5.8", + "postcss": "^8.5.26", "react": "^19.2.4", "react-dom": "^19.2.4", "react-hook-form": "^7.72.0", "sonner": "^2.0.7", "swr": "^2.4.1", "tailwindcss": "^4.2.2", - "uuid": "^13.0.0", + "uuid": "^13.0.2", "zod": "^4.3.6" }, "devDependencies": { @@ -46,9 +46,9 @@ "@types/react-dom": "^19", "autoprefixer": "^10.4.27", "eslint": "^9", - "eslint-config-next": "^15.4.6", + "eslint-config-next": "^15.5.23", "prettier-plugin-tailwindcss": "^0.7.2", - "prisma": "^7.7.0", + "prisma": "^7.9.1", "typescript": "^5" } }, @@ -75,33 +75,33 @@ } }, "node_modules/@electric-sql/pglite": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.4.1.tgz", - "integrity": "sha512-mZ9NzzUSYPOCnxHH1oAHPRzoMFJHY472raDKwXl/+6oPbpdJ7g8LsCN4FSaIIfkiCKHhb3iF/Zqo3NYxaIhU7Q==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite/-/pglite-0.4.3.tgz", + "integrity": "sha512-ichuWTgtd4mOM1G4SpyGJa5trT03lWbMypDV0fUXUCXg5hiHqVAz/bZyV68NqmkLB7WcYmj1RMJVSp8HV/v/ZQ==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@electric-sql/pglite-socket": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.1.1.tgz", - "integrity": "sha512-p2hoXw3Z3LQHwTeikdZNsFBOvXGqKY2hk51BBw+8NKND8eoH+8LFOtW9Z8CQKmTJ2qqGYu82ipqiyFZOTTXNfw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-socket/-/pglite-socket-0.1.3.tgz", + "integrity": "sha512-LAciWM0M1dCL8hlsxu2venbVZcdxema0BtDfpWYVqr+Y468UADw0pFWidhKw1M8sfJ8rdLT71tjMmnirf/IZRQ==", "devOptional": true, "license": "Apache-2.0", "bin": { "pglite-server": "dist/scripts/server.js" }, "peerDependencies": { - "@electric-sql/pglite": "0.4.1" + "@electric-sql/pglite": "0.4.3" } }, "node_modules/@electric-sql/pglite-tools": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.3.1.tgz", - "integrity": "sha512-C+T3oivmy9bpQvSxVqXA1UDY8cB9Eb9vZHL9zxWwEUfDixbXv4G3r2LjoTdR33LD8aomR3O9ZXEO3XEwr/cUCA==", + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/@electric-sql/pglite-tools/-/pglite-tools-0.3.3.tgz", + "integrity": "sha512-AlzLJTRJ8+UFgK8CmxIpyIpJ0+YaFw02IiOSdYrqxwPXdSyeIShz8aa9Tq+tYFXdPwcaMp/Fc80mQZ1dkOQ/wg==", "devOptional": true, "license": "Apache-2.0", "peerDependencies": { - "@electric-sql/pglite": "0.4.1" + "@electric-sql/pglite": "0.4.3" } }, "node_modules/@emnapi/core": { @@ -339,19 +339,6 @@ "node": ">=18.0.0" } }, - "node_modules/@hono/node-server": { - "version": "1.19.11", - "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", - "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18.14.1" - }, - "peerDependencies": { - "hono": "^4" - } - }, "node_modules/@hookform/resolvers": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.2.2.tgz", @@ -1001,13 +988,6 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@kurkle/color": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@kurkle/color/-/color-0.3.4.tgz", - "integrity": "sha512-M5UknZPHRu3DEDWoipU6sE8PdkZ6Z/S+v4dD+Ke8IaNlpdSQah50lz1KtcFBa2vsdOnwbbnxJwVM4wty6udA5w==", - "devOptional": true, - "license": "MIT" - }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.12", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.12.tgz", @@ -1032,15 +1012,15 @@ } }, "node_modules/@next/env": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.14.tgz", - "integrity": "sha512-aXeirLYuASxEgi4X4WhfXsShCFxWDfNn/8ZeC5YXAS2BB4A8FJi1kwwGL6nvMVboE7fZCzmJPNdMvVHc8JpaiA==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.23.tgz", + "integrity": "sha512-Mv3Z9hVbFcPnoLevsZ6rnX1TBtyHb5E17yN7HTPDXSXxeNsGBjUFrdbjRXKKXIOhfth7/cg6Ay7PZ2UFawaWsQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.14.tgz", - "integrity": "sha512-ogBjgsFrPPz19abP3VwcYSahbkUOMMvJjxCOYWYndw+PydeMuLuB4XrvNkNutFrTjC9St2KFULRdKID8Sd/CMQ==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.23.tgz", + "integrity": "sha512-0KnCFpiWVIsbwBhByZ0uIcjYM5xqGrFzN2eOPbwru/wuy5Z1dmA+3gP+PRbi4gl1Ny7an66BqAM/NHkX/50rbw==", "dev": true, "license": "MIT", "dependencies": { @@ -1048,9 +1028,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.14.tgz", - "integrity": "sha512-Y9K6SPzobnZvrRDPO2s0grgzC+Egf0CqfbdvYmQVaztV890zicw8Z8+4Vqw8oPck8r1TjUHxVh8299Cg4TrxXg==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.23.tgz", + "integrity": "sha512-SrEwOROH/rhA03F59hHtdhgtfZMWGzr5duDBWgRQt2rS3mJhqMKOcnNx6txOd0/i3E3D3uFKYFvyHsEiwQxzag==", "cpu": [ "arm64" ], @@ -1064,9 +1044,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.14.tgz", - "integrity": "sha512-aNnkSMjSFRTOmkd7qoNI2/rETQm/vKD6c/Ac9BZGa9CtoOzy3c2njgz7LvebQJ8iPxdeTuGnAjagyis8a9ifBw==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.23.tgz", + "integrity": "sha512-f0FpFbG2EhDCuptBGcfrLcYMDuQAhe6m1QA4VVfXFrIBoFXvXt/olGbBkYkloKlXQtmhuzvtdYyuu/6zf07GIg==", "cpu": [ "x64" ], @@ -1080,9 +1060,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.14.tgz", - "integrity": "sha512-tjlpia+yStPRS//6sdmlVwuO1Rioern4u2onafa5n+h2hCS9MAvMXqpVbSrjgiEOoCs0nJy7oPOmWgtRRNSM5Q==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.23.tgz", + "integrity": "sha512-WlNtfepUXKX2u2ZsJZ8c3c8+tJSRZqsYzoMwLOY72A8ucKCCgxgNhiePA3qzFYahVWrwcQd8jOeJmBinc+VFVQ==", "cpu": [ "arm64" ], @@ -1099,9 +1079,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.14.tgz", - "integrity": "sha512-8B8cngBaLadl5lbDRdxGCP1Lef8ipD6KlxS3v0ElDAGil6lafrAM3B258p1KJOglInCVFUjk751IXMr2ixeQOQ==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.23.tgz", + "integrity": "sha512-W/6qKk7UG93mg14PmQC+2urt69MIdwTBLNQ6MJyeC4wOCIHCjz+VfgssvS1pK7mgYBtLC1g6VKNoHD9xB0WWGg==", "cpu": [ "arm64" ], @@ -1118,9 +1098,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.14.tgz", - "integrity": "sha512-bAS6tIAg8u4Gn3Nz7fCPpSoKAexEt2d5vn1mzokcqdqyov6ZJ6gu6GdF9l8ORFrBuRHgv3go/RfzYz5BkZ6YSQ==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.23.tgz", + "integrity": "sha512-vzefI32mi6VMk96RaTAyxApgfGbiFzQBXVsekEjsDv1fr48mlABTWx0sUYhaYCBHWqCalxmz3DxbxFcbFvzNtw==", "cpu": [ "x64" ], @@ -1137,9 +1117,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.14.tgz", - "integrity": "sha512-mMxv/FcrT7Gfaq4tsR22l17oKWXZmH/lVqcvjX0kfp5I0lKodHYLICKPoX1KRnnE+ci6oIUdriUhuA3rBCDiSw==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.23.tgz", + "integrity": "sha512-qppK/3dTGOTI+aoWWBZc3DshFIhrzgL8guATlaN9V6M1QJxbkP/rhEZ22tdICsQ/2WWXopMZ2Jokzj2u3uKY3Q==", "cpu": [ "x64" ], @@ -1156,9 +1136,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.14.tgz", - "integrity": "sha512-OTmiBlYThppnvnsqx0rBqjDRemlmIeZ8/o4zI7veaXoeO1PVHoyj2lfTfXTiiGjCyRDhA10y4h6ZvZvBiynr2g==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.23.tgz", + "integrity": "sha512-Wc29KFOdT7XBcII3Vtmw7aoU8Uk3Mes/FNJfhFeSHdYBFJWMcR/DsI8U9BCPUhq/uycsUVuqSKGthW15tLsigA==", "cpu": [ "arm64" ], @@ -1172,9 +1152,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.14.tgz", - "integrity": "sha512-+W7eFf3RS7m4G6tppVTOSyP9Y6FsJXfOuKzav1qKniiFm3KFByQfPEcouHdjlZmysl4zJGuGLQ/M9XyVeyeNEg==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.23.tgz", + "integrity": "sha512-/C7wRW4fa9s/PKA18zGPPpVmx8ycgVpP8yOxro4gzGTzjPJdscbAP3ODeFvgiIovxD176Z2J/SXO9t8PJKHLeQ==", "cpu": [ "x64" ], @@ -1266,24 +1246,24 @@ } }, "node_modules/@prisma/adapter-pg": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.7.0.tgz", - "integrity": "sha512-q33Ta8sKbgzEpAy0lx45tAq//yMv0qcb+8nj+TCA3P4wiAY+OBFEFk/NDkZncAfHaNJeGo5WJpJdpbL+ijYx8g==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/adapter-pg/-/adapter-pg-7.9.1.tgz", + "integrity": "sha512-Ho2RK1KanQxLNSC0sR5bpiiVep10sWPLXCcxK+KXfI/Q69TMRbiafSvLPv3V9snimX72rMCqGlyJ4sBO4lKTAw==", "license": "Apache-2.0", "dependencies": { - "@prisma/driver-adapter-utils": "7.7.0", + "@prisma/driver-adapter-utils": "7.9.1", "@types/pg": "^8.16.0", "pg": "^8.16.3", "postgres-array": "3.0.4" } }, "node_modules/@prisma/client": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.7.0.tgz", - "integrity": "sha512-5Ar4OsZpJ54s21sy5oDNNW9gQtd4NuxCaiM7+JDTOU07D6VvlpLjYzAVCMB1+JzokN+08dAVomlx+b7bhJd3ww==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/client/-/client-7.9.1.tgz", + "integrity": "sha512-+xgrh2EhJVF79wC0yX5G4PI1Rdcm7Qn/nekNQ+t/O153wtNggruHal+fXHSa0QE+Tp/Cw5wvxeCEhZZ59xGm8Q==", "license": "Apache-2.0", "dependencies": { - "@prisma/client-runtime-utils": "7.7.0" + "@prisma/client-runtime-utils": "7.9.1" }, "engines": { "node": "^20.19 || ^22.12 || >=24.0" @@ -1302,116 +1282,114 @@ } }, "node_modules/@prisma/client-runtime-utils": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.7.0.tgz", - "integrity": "sha512-BLyd0UpFYOtyJFTHm7jS9vesHW7P83abibodQMiIofqjBKzDHQ1VAsQkdfvXyYDkPlONPfOTz7/rv3x/+CQqvQ==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/client-runtime-utils/-/client-runtime-utils-7.9.1.tgz", + "integrity": "sha512-mVIBGYdO5CFmK0HvjxrtfIyQQcPdb88pSCeVQriVQPVZyDovIWblpHfOgcS8QO187j3QF0ePArH8qPhp0AU2vg==", "license": "Apache-2.0" }, "node_modules/@prisma/config": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.7.0.tgz", - "integrity": "sha512-hmPI3tKLO2aP0Y5vugbjcnA9qqlfJndiT6ds4tw28U5hNHLWg+mHJEWAhjsSPgxjtmxhJ/EDIeIlyh+3Us0OPg==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/config/-/config-7.9.1.tgz", + "integrity": "sha512-4znKhxTmXmuPye9Z6pbIyYb5VZlkZ05qG1L6Dr4g+7oTwc6V50Bs9XirFBDdjWt+H/AabMn9aUnxBcvj8z05aA==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "c12": "3.1.0", + "c12": "3.3.4", "deepmerge-ts": "7.1.5", "effect": "3.20.0", "empathic": "2.0.0" } }, "node_modules/@prisma/debug": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.7.0.tgz", - "integrity": "sha512-12J62XdqCmpiwJHhHdQxZeY3ckVCWIFmcJP8hg5dPTceeiQ0wiojXGFYTluKqFQfu46fRLgb/rLALZMAx3+dTA==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/debug/-/debug-7.9.1.tgz", + "integrity": "sha512-/cpVZ4itxtcgB8GHBvZtcmuEjq+lWsLrRJxFMbwZrT1RIdtuKmUm7PPGo/wzfbYpBrk+9WmmBE8CHJw2rybKDQ==", "license": "Apache-2.0" }, "node_modules/@prisma/dev": { - "version": "0.24.3", - "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.24.3.tgz", - "integrity": "sha512-ffHlQuKXZiaDt9Go0OnCTdJZrHxK0k7omJKNV86/VjpsXu5EIHZLK0T7JSWgvNlJwh56kW9JFu9v0qJciFzepg==", + "version": "0.24.17", + "resolved": "https://registry.npmjs.org/@prisma/dev/-/dev-0.24.17.tgz", + "integrity": "sha512-UvdZzmpFwknnfreh6Jije84ekkYGPYEJhXG1tFzCsCfQyzJifrOo38eZc0qajzvaC6OLUOrN9ML5XfCnEZL9DA==", "devOptional": true, "license": "ISC", "dependencies": { - "@electric-sql/pglite": "0.4.1", - "@electric-sql/pglite-socket": "0.1.1", - "@electric-sql/pglite-tools": "0.3.1", - "@hono/node-server": "1.19.11", + "@electric-sql/pglite": "0.4.3", + "@electric-sql/pglite-socket": "0.1.3", + "@electric-sql/pglite-tools": "0.3.3", "@prisma/get-platform": "7.2.0", "@prisma/query-plan-executor": "7.2.0", - "@prisma/streams-local": "0.1.2", + "@prisma/streams-local": "0.1.11", + "find-my-way": "9.7.0", "foreground-child": "3.3.1", "get-port-please": "3.2.0", - "hono": "^4.12.8", - "http-status-codes": "2.3.0", "pathe": "2.0.3", "proper-lockfile": "4.1.2", "remeda": "2.33.4", "std-env": "3.10.0", - "valibot": "1.2.0", + "valibot": "1.4.2", "zeptomatch": "2.1.0" } }, "node_modules/@prisma/driver-adapter-utils": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.7.0.tgz", - "integrity": "sha512-gZXREeu6mOk7zXfGFJgh86p7Vhj0sXNKp+4Cg1tWYo7V2dfncP2qxS2BiTmbIIha8xPqItkl0WSw38RuSq1HoQ==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/driver-adapter-utils/-/driver-adapter-utils-7.9.1.tgz", + "integrity": "sha512-vmHehG7nn/heW32DXXpp13DxxAxVVe6n250oEt3dOL2E/4bt3olktKZN0mzSuxMMronyMSkbeW2uCOn3F4g8RQ==", "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "7.7.0" + "@prisma/debug": "7.9.1" } }, "node_modules/@prisma/engines": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.7.0.tgz", - "integrity": "sha512-7fmcbT7HHXBq/b+3h/dO1JI3fd8l8q7erf7xP7pRprh58hmSSnG8mg9K3yjW3h9WaHWUwngVFpSxxxivaitQ2w==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/engines/-/engines-7.9.1.tgz", + "integrity": "sha512-UprXSMNXx2NF5ow4pqaQtE8OuBz6K78B0wc0tn2L28G5r933iWp1DR9Do2qWrsNvvFIP3x6mpEWnQtckMO0Uhg==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "7.7.0", - "@prisma/engines-version": "7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711", - "@prisma/fetch-engine": "7.7.0", - "@prisma/get-platform": "7.7.0" + "@prisma/debug": "7.9.1", + "@prisma/engines-version": "7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad", + "@prisma/fetch-engine": "7.9.1", + "@prisma/get-platform": "7.9.1" } }, "node_modules/@prisma/engines-version": { - "version": "7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711", - "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711.tgz", - "integrity": "sha512-r51DLcJ8bDRSrBEJF3J4cinoWyGA7rfP2mG6lD90VqIbGNOkbfcLcXalSVjq5Y6brQS3vcjrq4GbyUb1Cb7vkw==", + "version": "7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad", + "resolved": "https://registry.npmjs.org/@prisma/engines-version/-/engines-version-7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad.tgz", + "integrity": "sha512-2BsPPFksz3CQUXG6af3rVCtJKg6+JJGJTtfgu2fU8DdXhOfkBjulCq8mwybCd6ge0/jhZq2kOtLAbmUDMyI1nA==", "devOptional": true, "license": "Apache-2.0" }, "node_modules/@prisma/engines/node_modules/@prisma/get-platform": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.7.0.tgz", - "integrity": "sha512-MEUNzvKxvYnJ7kgvd6oNRnMmmiGNS9TYLB2weMeIXplnHdL/UWEGnvavYGnN7KLJ2n0iI4dDAyzSkHI3c7AscQ==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.9.1.tgz", + "integrity": "sha512-PK8R60YZRQvYxBrGG9i7l2/rFyzy+2MuI1dKtmtrCqPH8YpiJx/MfiC7LRzX5786rZDEv7BngcjfIJW4/9ADuw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "7.7.0" + "@prisma/debug": "7.9.1" } }, "node_modules/@prisma/fetch-engine": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.7.0.tgz", - "integrity": "sha512-TfyzveBQoK4xALzsTpVhB/0KG1N8zOK0ap+RnBMkzGUu3f98fnQ4QtXa2wlKPhsO2X8a3N5ugFQgcKNoHGmDfw==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/fetch-engine/-/fetch-engine-7.9.1.tgz", + "integrity": "sha512-9DwxrNTeT25Orbu9CWh0CZvVlyY1lmscpbaeLZcOnuR7zcuFrt91YSmmOfIm7zJ08YOZ6mVzURKwLoMwEBcK8w==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "7.7.0", - "@prisma/engines-version": "7.6.0-1.75cbdc1eb7150937890ad5465d861175c6624711", - "@prisma/get-platform": "7.7.0" + "@prisma/debug": "7.9.1", + "@prisma/engines-version": "7.9.0-1.e922089b7d7502aff4249d5da3420f6fa55fc6ad", + "@prisma/get-platform": "7.9.1" } }, "node_modules/@prisma/fetch-engine/node_modules/@prisma/get-platform": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.7.0.tgz", - "integrity": "sha512-MEUNzvKxvYnJ7kgvd6oNRnMmmiGNS9TYLB2weMeIXplnHdL/UWEGnvavYGnN7KLJ2n0iI4dDAyzSkHI3c7AscQ==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/@prisma/get-platform/-/get-platform-7.9.1.tgz", + "integrity": "sha512-PK8R60YZRQvYxBrGG9i7l2/rFyzy+2MuI1dKtmtrCqPH8YpiJx/MfiC7LRzX5786rZDEv7BngcjfIJW4/9ADuw==", "devOptional": true, "license": "Apache-2.0", "dependencies": { - "@prisma/debug": "7.7.0" + "@prisma/debug": "7.9.1" } }, "node_modules/@prisma/get-platform": { @@ -1439,9 +1417,9 @@ "license": "Apache-2.0" }, "node_modules/@prisma/streams-local": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@prisma/streams-local/-/streams-local-0.1.2.tgz", - "integrity": "sha512-l49yTxKKF2odFxaAXTmwmkBKL3+bVQ1tFOooGifu4xkdb9NMNLxHj27XAhTylWZod8I+ISGM5erU1xcl/oBCtg==", + "version": "0.1.11", + "resolved": "https://registry.npmjs.org/@prisma/streams-local/-/streams-local-0.1.11.tgz", + "integrity": "sha512-0TcebL559MByKqTJ+SsrFIEg228iw8UCVRFckzgfRSiJqczhs+MuAgWOF9lnOIV/IVqvu+KMnFTH0eDeTQMpUg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { @@ -1451,14 +1429,14 @@ "proper-lockfile": "^4.1.2" }, "engines": { - "bun": ">=1.3.6", + "bun": ">=1.2.0", "node": ">=22.0.0" } }, "node_modules/@prisma/streams-local/node_modules/ajv": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", - "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1480,14 +1458,23 @@ "license": "MIT" }, "node_modules/@prisma/studio-core": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.27.3.tgz", - "integrity": "sha512-AADjNFPdsrglxHQVTmHFqv6DuKQZ5WY4p5/gVFY017twvNrSwpLJ9lqUbYYxEu2W7nbvVxTZA8deJ8LseNALsw==", + "version": "0.33.0", + "resolved": "https://registry.npmjs.org/@prisma/studio-core/-/studio-core-0.33.0.tgz", + "integrity": "sha512-V2fX/nKEymNTrHXwfP26PGjoLStO35Ogu+ex7CFJbLrMYEcZxxZpiSNOs7px23Hk5mzLWvM5RsqG6Ka+rha+wg==", "devOptional": true, "license": "Apache-2.0", "dependencies": { "@radix-ui/react-toggle": "1.1.10", - "chart.js": "4.5.1" + "@visx/curve": "4.0.1-alpha.0", + "@visx/event": "4.0.1-alpha.0", + "@visx/grid": "4.0.1-alpha.0", + "@visx/group": "4.0.1-alpha.0", + "@visx/responsive": "4.0.1-alpha.0", + "@visx/scale": "4.0.1-alpha.0", + "@visx/shape": "4.0.1-alpha.0", + "d3-array": "3.2.4", + "d3-shape": "3.2.0", + "elkjs": "0.11.1" }, "engines": { "node": "^20.19 || ^22.12 || >=24.0", @@ -2074,6 +2061,95 @@ "tslib": "^2.4.0" } }, + "node_modules/@types/d3-array": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz", + "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", + "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/d3-format": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", + "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz", + "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-shape": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz", + "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@types/d3-time-format": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-2.1.0.tgz", + "integrity": "sha512-/myT3I7EwlukNOX2xVdMzb8FRgNzRMpsZddwst9Ld/VFe6LyJyRp0s32l/V9XoUzk+Gqu56F/oGk6507+8BxrA==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", @@ -2091,6 +2167,13 @@ "@types/node": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.16", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.16.tgz", + "integrity": "sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -2105,6 +2188,13 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.25", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.25.tgz", + "integrity": "sha512-+K1NIO8I+F9/wNulfVvu23QYd0Pe9/OCqRrim4NoYIf1VoEDL90Ve4ClzpyqBLc7NpGGWRvYNCKZ1BE/Jpf8dQ==", + "devOptional": true, + "license": "MIT" + }, "node_modules/@types/node": { "version": "20.19.39", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.39.tgz", @@ -2353,16 +2443,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", - "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { - "node": "18 || 20 || >=22" + "node": "20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { @@ -2729,6 +2819,157 @@ "win32" ] }, + "node_modules/@visx/curve": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/curve/-/curve-4.0.1-alpha.0.tgz", + "integrity": "sha512-jRu61Uz274pV1zyioXmboyrLutYbnKsgjj4njSGCnhdXj5GkZvZbg+ThDb6oOzoAnJOBRLz4rzPlWvNJOzuVMg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@visx/vendor": "4.0.0-alpha.0" + } + }, + "node_modules/@visx/event": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/event/-/event-4.0.1-alpha.0.tgz", + "integrity": "sha512-EQqCMSv/s8NbFjo+hz3FKsvvYfP+2QslsFJ/24/O5l/W+7UC6J6aAvO0ujVwrTwdYbuQ+vhxKi1xdPdKR/qj1g==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "@visx/point": "4.0.1-alpha.0" + } + }, + "node_modules/@visx/grid": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/grid/-/grid-4.0.1-alpha.0.tgz", + "integrity": "sha512-rycutGmTHO+znNdPumheWMglm7YfpffvRwUkVy5zy4WoORIuKTMkDxwnOzHG2xMxU3EE/YCd37xFV5AxA30yeg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "@visx/curve": "4.0.1-alpha.0", + "@visx/group": "4.0.1-alpha.0", + "@visx/point": "4.0.1-alpha.0", + "@visx/scale": "4.0.1-alpha.0", + "@visx/shape": "4.0.1-alpha.0", + "classnames": "^2.3.1" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0" + } + }, + "node_modules/@visx/group": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/group/-/group-4.0.1-alpha.0.tgz", + "integrity": "sha512-V19l7iQ7jccBv8kao/EByuI6o4xtxzzLV9nqVI1hRvmdzTVsuLpqlwzYCZUXJaTVvUWf8s4D2SQFjGkj/Nw+0w==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/react": "*", + "classnames": "^2.3.1" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0" + } + }, + "node_modules/@visx/point": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/point/-/point-4.0.1-alpha.0.tgz", + "integrity": "sha512-ijTfr/Nx09f03vIj9nyTr3z4Xth4Y75427UaogJh6dnIRLMEFHQOwNu791sbfiNj0a+ZXuaE32h0vKrFe4/8Qg==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/@visx/responsive": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/responsive/-/responsive-4.0.1-alpha.0.tgz", + "integrity": "sha512-o+1zGywQZY0+yOx3Iw87wc4bbPJRr/HnIukTwfOz4UVyj9pB1OQNVHB7OORO1+LBHJceWpB31co/ZV9KHncKrA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "^4.17.13", + "@types/react": "*", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0" + } + }, + "node_modules/@visx/scale": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/scale/-/scale-4.0.1-alpha.0.tgz", + "integrity": "sha512-nzjeE87vFSAXGWFiiNfBpNLAf0Q8Qmf6syvKLjqNi4kGZkdhbUll3E/59YsgWXmjM8+llPLWzGsP+JPvo5eq1A==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@visx/vendor": "4.0.0-alpha.0" + } + }, + "node_modules/@visx/shape": { + "version": "4.0.1-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/shape/-/shape-4.0.1-alpha.0.tgz", + "integrity": "sha512-62QeiVNmPlterQGwhkEDcbq7M0MqY0lBsK5QKXtM9ZoPZWkuGV3aykA3+Xu20B2FAvyJq4LqJzBc7Sxr+EAdbA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "@types/lodash": "^4.17.13", + "@types/react": "*", + "@visx/curve": "4.0.1-alpha.0", + "@visx/group": "4.0.1-alpha.0", + "@visx/scale": "4.0.1-alpha.0", + "@visx/vendor": "4.0.0-alpha.0", + "classnames": "^2.3.1", + "lodash": "^4.17.21" + }, + "peerDependencies": { + "react": "^16.14.0 || ^17.0.0-0 || ^18.0.0-0 || ^19.0.0-0" + } + }, + "node_modules/@visx/vendor": { + "version": "4.0.0-alpha.0", + "resolved": "https://registry.npmjs.org/@visx/vendor/-/vendor-4.0.0-alpha.0.tgz", + "integrity": "sha512-6I+MuqXBcv9jnlcVowHoHKSdk9gXTWkHLKyqBwRWg7LY6A3Ei8SHfubpqGV5rBUSppxMq2RszPJUS6w+H0YgmQ==", + "devOptional": true, + "license": "MIT and ISC", + "dependencies": { + "@types/d3-array": "3.0.3", + "@types/d3-color": "3.1.0", + "@types/d3-delaunay": "6.0.1", + "@types/d3-format": "3.0.1", + "@types/d3-geo": "3.1.0", + "@types/d3-interpolate": "3.0.1", + "@types/d3-path": "3.1.1", + "@types/d3-scale": "4.0.2", + "@types/d3-shape": "3.1.7", + "@types/d3-time": "3.0.0", + "@types/d3-time-format": "2.1.0", + "d3-array": "3.2.1", + "d3-color": "3.1.0", + "d3-delaunay": "6.0.2", + "d3-format": "3.1.0", + "d3-geo": "3.1.0", + "d3-interpolate": "3.0.1", + "d3-path": "3.1.0", + "d3-scale": "4.0.2", + "d3-shape": "3.2.0", + "d3-time": "3.1.0", + "d3-time-format": "4.1.0", + "internmap": "2.0.3" + } + }, + "node_modules/@visx/vendor/node_modules/d3-array": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.1.tgz", + "integrity": "sha512-gUY/qeHq/yNqqoCKNq4vtpFLdoCdvyNpWoC/KNjhGbhDuQpAM9sIQQKkXSNpXa9h5KySs/gzm7R88WkUutgwWQ==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", @@ -3089,16 +3330,16 @@ } }, "node_modules/better-result": { - "version": "2.8.1", - "resolved": "https://registry.npmjs.org/better-result/-/better-result-2.8.1.tgz", - "integrity": "sha512-C4FQ1gCLz1YCxmM8HhNPb4D7WQmdrdllkhNReeLwvIVtJKQFKKfwJwmM3yZEBG4P34cLtrgB+FEPr1u553hF7Q==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/better-result/-/better-result-2.10.0.tgz", + "integrity": "sha512-oQhh0y1qo2/ZKdAAEvHZAqKKiHOFU5k/bW96fE2ScgQOVkJRiHwB+nOS1SgFsYqRlxMDWvefXi9Q3px7QvgNDw==", "devOptional": true, "license": "MIT" }, "node_modules/brace-expansion": { - "version": "1.1.13", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", - "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -3154,27 +3395,27 @@ } }, "node_modules/c12": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/c12/-/c12-3.1.0.tgz", - "integrity": "sha512-uWoS8OU1MEIsOv8p/5a82c3H31LsWVR5qiyXVfBNOzfffjUWtPnhAb4BYI2uG2HfGmZmFjCtui5XNWaps+iFuw==", + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/c12/-/c12-3.3.4.tgz", + "integrity": "sha512-cM0ApFQSBXuourJejzwv/AuPRvAxordTyParRVcHjjtXirtkzM0uK2L9TTn9s0cXZbG7E55jCivRQzoxYmRAlA==", "devOptional": true, "license": "MIT", "dependencies": { - "chokidar": "^4.0.3", - "confbox": "^0.2.2", - "defu": "^6.1.4", - "dotenv": "^16.6.1", - "exsolve": "^1.0.7", - "giget": "^2.0.0", - "jiti": "^2.4.2", + "chokidar": "^5.0.0", + "confbox": "^0.2.4", + "defu": "^6.1.6", + "dotenv": "^17.3.1", + "exsolve": "^1.0.8", + "giget": "^3.2.0", + "jiti": "^2.6.1", "ohash": "^2.0.11", "pathe": "^2.0.3", - "perfect-debounce": "^1.0.0", - "pkg-types": "^2.2.0", - "rc9": "^2.1.2" + "perfect-debounce": "^2.1.0", + "pkg-types": "^2.3.0", + "rc9": "^3.0.1" }, "peerDependencies": { - "magicast": "^0.3.5" + "magicast": "*" }, "peerDependenciesMeta": { "magicast": { @@ -3279,44 +3520,28 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chart.js": { - "version": "4.5.1", - "resolved": "https://registry.npmjs.org/chart.js/-/chart.js-4.5.1.tgz", - "integrity": "sha512-GIjfiT9dbmHRiYi6Nl2yFCq7kkwdkp1W/lp2J99rX0yo9tgJGn3lKQATztIjb5tVtevcBtIdICNWqlq5+E8/Pw==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "@kurkle/color": "^0.3.0" - }, - "engines": { - "pnpm": ">=8" - } - }, "node_modules/chokidar": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", - "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", + "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "devOptional": true, "license": "MIT", "dependencies": { - "readdirp": "^4.0.1" + "readdirp": "^5.0.0" }, "engines": { - "node": ">= 14.16.0" + "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, - "node_modules/citty": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.1.6.tgz", - "integrity": "sha512-tskPPKEs8D2KPafUypv2gxwJP8h/OaJmC82QQGGDQcHvXX43xF2VDACcJVmZ0EuSxkpO9Kc4MlrA3q0+FG58AQ==", + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", "devOptional": true, - "license": "MIT", - "dependencies": { - "consola": "^3.2.3" - } + "license": "MIT" }, "node_modules/client-only": { "version": "0.0.1", @@ -3358,16 +3583,6 @@ "devOptional": true, "license": "MIT" }, - "node_modules/consola": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", - "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": "^14.18.0 || >=16.10.0" - } - }, "node_modules/cookie": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", @@ -3403,6 +3618,144 @@ "devOptional": true, "license": "MIT" }, + "node_modules/d3-array": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", + "integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-format": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-geo": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.0.tgz", + "integrity": "sha512-JEo5HxXDdDYXCaWdwLRt79y7giK8SbhZJbFWXqbRTolCHFI5jRqteLzCsq51NKbUoX0PjBVSohxrx+NoOUujYA==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", + "integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-time-format": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -3542,6 +3895,16 @@ "devOptional": true, "license": "MIT" }, + "node_modules/delaunator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.1.0.tgz", + "integrity": "sha512-AGrQ4QSgssa1NGmWmLPqN5NY2KajF5MqxetNEO+o0n3ZwZZeTmt7bBnvzHWrmkZFxGgr4HdyFgelzgi06otLuQ==", + "devOptional": true, + "license": "ISC", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/denque": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/denque/-/denque-2.1.0.tgz", @@ -3602,9 +3965,9 @@ } }, "node_modules/dotenv": { - "version": "16.6.1", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", - "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "version": "17.4.2", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.4.2.tgz", + "integrity": "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw==", "devOptional": true, "license": "BSD-2-Clause", "engines": { @@ -3647,6 +4010,13 @@ "dev": true, "license": "ISC" }, + "node_modules/elkjs": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/elkjs/-/elkjs-0.11.1.tgz", + "integrity": "sha512-zxxR9k+rx5ktMwT/FwyLdPCrq7xN6e4VGGHH8hA01vVYKjTFik7nHOxBnAYtrgYUB1RpAiLvA1/U2YraWxyKKg==", + "devOptional": true, + "license": "EPL-2.0" + }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", @@ -3953,13 +4323,13 @@ } }, "node_modules/eslint-config-next": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.14.tgz", - "integrity": "sha512-lmJ5F8ZgOYogq0qtH4L5SpxuASY2SPdOzqUprN2/56+P3GPsIpXaUWIJC66kYIH+yZdsM4nkHE5MIBP6s1NiBw==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.23.tgz", + "integrity": "sha512-z4WcTXNqFHwMG4V8WHb2xrlEPJvwarZa+H/6CR28vxr53icRnQzGXviO11p748BwrMZGl52itdPRzZfzYo0SKw==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.5.14", + "@next/eslint-plugin-next": "15.5.23", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -4300,9 +4670,9 @@ } }, "node_modules/exsolve": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", - "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.1.1.tgz", + "integrity": "sha512-9U/jZUgjnSGyntRr6y5Muu1MJcwFl6kPu7k8qLF0IMNfLqvw0NZ4nnVDq0RVoZ0RvCyumib4Ez3KYrVfilrw+g==", "devOptional": true, "license": "MIT" }, @@ -4329,6 +4699,13 @@ "node": ">=8.0.0" } }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "devOptional": true, + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -4380,10 +4757,20 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, "node_modules/fast-uri": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", - "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "devOptional": true, "funding": [ { @@ -4433,6 +4820,21 @@ "node": ">=8" } }, + "node_modules/find-my-way": { + "version": "9.7.0", + "resolved": "https://registry.npmjs.org/find-my-way/-/find-my-way-9.7.0.tgz", + "integrity": "sha512-f2JHn75x2JlwUwLenZypgczR7YWMb/uO9BvUXtus+JMgkbIkLADd38cI4EiV+OQqrGo1Zlq6V8wnqMJ8e62wUQ==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^5.0.0" + }, + "engines": { + "node": ">=20" + } + }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -4674,19 +5076,11 @@ } }, "node_modules/giget": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/giget/-/giget-2.0.0.tgz", - "integrity": "sha512-L5bGsVkxJbJgdnwyuheIunkGatUF/zssUoxxjACCseZYAVbaqdh9Tsmmlkl8vYan09H7sbvKt4pS8GqKLBrEzA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", + "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", "devOptional": true, "license": "MIT", - "dependencies": { - "citty": "^0.1.6", - "consola": "^3.4.0", - "defu": "^6.1.4", - "node-fetch-native": "^1.6.6", - "nypm": "^0.6.0", - "pathe": "^2.0.3" - }, "bin": { "giget": "dist/cli.mjs" } @@ -4755,9 +5149,9 @@ "license": "ISC" }, "node_modules/grammex": { - "version": "3.1.12", - "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.12.tgz", - "integrity": "sha512-6ufJOsSA7LcQehIJNCO7HIBykfM7DXQual0Ny780/DEcJIpBlHRvcqEBWGPYd7hrXL2GJ3oJI1MIhaXjWmLQOQ==", + "version": "3.1.13", + "resolved": "https://registry.npmjs.org/grammex/-/grammex-3.1.13.tgz", + "integrity": "sha512-LnPnhOBLEJEVKS8WFDVaA397L9Kq55Q9oSITJiVLHVdhAclfUkWzQv74KhvZHKL2Q09Pb1XdsrOsZ4LfTFFTEg==", "devOptional": true, "license": "MIT" }, @@ -4868,23 +5262,6 @@ "node": ">= 0.4" } }, - "node_modules/hono": { - "version": "4.12.12", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.12.tgz", - "integrity": "sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=16.9.0" - } - }, - "node_modules/http-status-codes": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", - "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "devOptional": true, - "license": "MIT" - }, "node_modules/iceberg-js": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/iceberg-js/-/iceberg-js-0.8.1.tgz", @@ -4963,6 +5340,16 @@ "node": ">= 0.4" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "devOptional": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, "node_modules/is-array-buffer": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", @@ -5431,10 +5818,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", - "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -5826,6 +6223,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "devOptional": true, + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5999,9 +6403,9 @@ } }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "funding": [ { "type": "github", @@ -6040,12 +6444,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.5.14", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.14.tgz", - "integrity": "sha512-M6S+4JyRjmKic2Ssm7jHUPkE6YUJ6lv4507jprsSZLulubz0ihO2E+S4zmQK3JZ2ov81JrugukKU4Tz0ivgqqQ==", + "version": "15.5.23", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.23.tgz", + "integrity": "sha512-Gvd2WKgvxIXCGotxcI1im/Uf3rS3J3oZGw0g/uskg6AVBZhyE3aAbujkYWzS3xLmEPEtTLfkaVQUKK0KMTSIkA==", "license": "MIT", "dependencies": { - "@next/env": "15.5.14", + "@next/env": "15.5.23", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -6058,14 +6462,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.14", - "@next/swc-darwin-x64": "15.5.14", - "@next/swc-linux-arm64-gnu": "15.5.14", - "@next/swc-linux-arm64-musl": "15.5.14", - "@next/swc-linux-x64-gnu": "15.5.14", - "@next/swc-linux-x64-musl": "15.5.14", - "@next/swc-win32-arm64-msvc": "15.5.14", - "@next/swc-win32-x64-msvc": "15.5.14", + "@next/swc-darwin-arm64": "15.5.23", + "@next/swc-darwin-x64": "15.5.23", + "@next/swc-linux-arm64-gnu": "15.5.23", + "@next/swc-linux-arm64-musl": "15.5.23", + "@next/swc-linux-x64-gnu": "15.5.23", + "@next/swc-linux-x64-musl": "15.5.23", + "@next/swc-win32-arm64-msvc": "15.5.23", + "@next/swc-win32-x64-msvc": "15.5.23", "sharp": "^0.34.3" }, "peerDependencies": { @@ -6092,9 +6496,9 @@ } }, "node_modules/next-auth": { - "version": "4.24.13", - "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.13.tgz", - "integrity": "sha512-sgObCfcfL7BzIK76SS5TnQtc3yo2Oifp/yIpfv6fMfeBOiBJkDWF3A2y9+yqnmJ4JKc2C+nMjSjmgDeTwgN1rQ==", + "version": "4.24.15", + "resolved": "https://registry.npmjs.org/next-auth/-/next-auth-4.24.15.tgz", + "integrity": "sha512-NnjYtjrSOAx/TIVFGTX4IfI/9yHnNpi4B7FuLUwuV20v2Zxgr2OGP/YN0ynJuI7y8QOnTBPitfOdEXZrVvhIuA==", "license": "ISC", "dependencies": { "@babel/runtime": "^7.20.13", @@ -6105,7 +6509,7 @@ "openid-client": "^5.4.0", "preact": "^10.6.3", "preact-render-to-string": "^5.1.19", - "uuid": "^8.3.2" + "uuid": "^11.1.1" }, "peerDependencies": { "@auth/core": "0.34.3", @@ -6133,12 +6537,16 @@ } }, "node_modules/next-auth/node_modules/uuid": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", - "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.1.tgz", + "integrity": "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], "license": "MIT", "bin": { - "uuid": "dist/bin/uuid" + "uuid": "dist/esm/bin/uuid" } }, "node_modules/next/node_modules/postcss": { @@ -6198,13 +6606,6 @@ "semver": "bin/semver.js" } }, - "node_modules/node-fetch-native": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz", - "integrity": "sha512-g9yhqoedzIUm0nTnTqAQvueMPVOuIY16bqgAJJC8XOOubYFNwz6IER9qs0Gq2Xd0+CecCKFjtdDTMA4u4xG06Q==", - "devOptional": true, - "license": "MIT" - }, "node_modules/node-releases": { "version": "2.0.37", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", @@ -6212,31 +6613,6 @@ "dev": true, "license": "MIT" }, - "node_modules/nypm": { - "version": "0.6.5", - "resolved": "https://registry.npmjs.org/nypm/-/nypm-0.6.5.tgz", - "integrity": "sha512-K6AJy1GMVyfyMXRVB88700BJqNUkByijGJM8kEHpLdcAt+vSQAVfkWWHYzuRXHSY6xA2sNc5RjTj0p9rE2izVQ==", - "devOptional": true, - "license": "MIT", - "dependencies": { - "citty": "^0.2.0", - "pathe": "^2.0.3", - "tinyexec": "^1.0.2" - }, - "bin": { - "nypm": "dist/cli.mjs" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/nypm/node_modules/citty": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", - "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", - "devOptional": true, - "license": "MIT" - }, "node_modules/oauth": { "version": "0.9.15", "resolved": "https://registry.npmjs.org/oauth/-/oauth-0.9.15.tgz", @@ -6531,9 +6907,9 @@ "license": "MIT" }, "node_modules/perfect-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", - "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-2.1.0.tgz", + "integrity": "sha512-LjgdTytVFXeUgtHZr9WYViYSM/g8MkcTPYDlPa3cDqMirHjKiSZPYd6DoL7pK8AJQr+uWkQvCjHNdiMqsrJs+g==", "devOptional": true, "license": "MIT" }, @@ -6655,14 +7031,14 @@ } }, "node_modules/pkg-types": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.0.tgz", - "integrity": "sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-2.3.1.tgz", + "integrity": "sha512-y+ichcgc2LrADuhLNAx8DFjVfgz91pRxfZdI3UDhxHvcVEZsenLO+7XaU5vOp0u/7V/wZ+plyuQxtrDlZJ+yeg==", "devOptional": true, "license": "MIT", "dependencies": { - "confbox": "^0.2.2", - "exsolve": "^1.0.7", + "confbox": "^0.2.4", + "exsolve": "^1.0.8", "pathe": "^2.0.3" } }, @@ -6677,9 +7053,9 @@ } }, "node_modules/postcss": { - "version": "8.5.9", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", - "integrity": "sha512-7a70Nsot+EMX9fFU3064K/kdHWZqGVY+BADLyXc8Dfv+mTLLVl6JzJpPaCZ2kQL9gIJvKXSLMHhqdRRjwQeFtw==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "funding": [ { "type": "opencollective", @@ -6696,7 +7072,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6899,17 +7275,17 @@ "license": "MIT" }, "node_modules/prisma": { - "version": "7.7.0", - "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.7.0.tgz", - "integrity": "sha512-HlgwRBt1uEFB9LStHL4HLYDvoi4BNu1rYA0hPG0zCAEyK9SaZBqp7E5Rjpc3Qh8Lex/ye/svoHZ0OWoFNhWxuQ==", + "version": "7.9.1", + "resolved": "https://registry.npmjs.org/prisma/-/prisma-7.9.1.tgz", + "integrity": "sha512-aPqePoZIqwlAchbgbFDO/wHqGB+7H1nj9gaM+OsL9h77S5S3TnLd9BgD3LnoeDikULo7cl2HSUrEyQ55Z7DYbg==", "devOptional": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@prisma/config": "7.7.0", - "@prisma/dev": "0.24.3", - "@prisma/engines": "7.7.0", - "@prisma/studio-core": "0.27.3", + "@prisma/config": "7.9.1", + "@prisma/dev": "0.24.17", + "@prisma/engines": "7.9.1", + "@prisma/studio-core": "0.33.0", "mysql2": "3.15.3", "postgres": "3.4.7" }, @@ -7012,14 +7388,14 @@ "license": "MIT" }, "node_modules/rc9": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/rc9/-/rc9-2.1.2.tgz", - "integrity": "sha512-btXCnMmRIBINM2LDZoEmOogIZU7Qe7zn4BpomSKZ/ykbLObuBdvG+mFq11DL6fjH1DRwHhrlgtYWG96bJiC7Cg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/rc9/-/rc9-3.0.1.tgz", + "integrity": "sha512-gMDyleLWVE+i6Sgtc0QbbY6pEKqYs97NGi6isHQPqYlLemPoO8dxQ3uGi0f4NiP98c+jMW6cG1Kx9dDwfvqARQ==", "devOptional": true, "license": "MIT", "dependencies": { - "defu": "^6.1.4", - "destr": "^2.0.3" + "defu": "^6.1.6", + "destr": "^2.0.5" } }, "node_modules/react": { @@ -7067,13 +7443,13 @@ "license": "MIT" }, "node_modules/readdirp": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", - "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.1.1.tgz", + "integrity": "sha512-Kko+Y5XQ6fM+Ce3dq3m9YGxnacYZYl9cA1wZjaF3Vbry2L3i1qVg8+CAgNPsXRArPMUMCaOR7oa9Nqntc43JKA==", "devOptional": true, "license": "MIT", "engines": { - "node": ">= 14.18.0" + "node": ">= 20.19.0" }, "funding": { "type": "individual", @@ -7188,6 +7564,16 @@ "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" } }, + "node_modules/ret": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", @@ -7209,6 +7595,13 @@ "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.3.tgz", + "integrity": "sha512-NS3levdsRIUOmiJ8FZWCP7LG3QpJyrs/TE0Zpf1yvZu8cAJJ6QMW92H1c7kWpdIHo8RvmLxN/o2JXTKHp74lUA==", + "devOptional": true, + "license": "Unlicense" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7288,6 +7681,29 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-regex2": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/safe-regex2/-/safe-regex2-5.1.1.tgz", + "integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==", + "devOptional": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "ret": "~0.5.0" + }, + "bin": { + "safe-regex2": "bin/safe-regex2.js" + } + }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -7810,16 +8226,6 @@ "url": "https://opencollective.com/webpack" } }, - "node_modules/tinyexec": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.1.1.tgz", - "integrity": "sha512-VKS/ZaQhhkKFMANmAOhhXVoIfBXblQxGX1myCQ2faQrfmobMftXeJPcZGp0gS07ocvGJWDLZGyOZDadDBqYIJg==", - "devOptional": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/tinyglobby": { "version": "0.2.16", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", @@ -8129,9 +8535,9 @@ } }, "node_modules/uuid": { - "version": "13.0.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.0.tgz", - "integrity": "sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==", + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-13.0.2.tgz", + "integrity": "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==", "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -8142,9 +8548,9 @@ } }, "node_modules/valibot": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.2.0.tgz", - "integrity": "sha512-mm1rxUsmOxzrwnX5arGS+U4T25RdvpPjPN4yR0u9pUBov9+zGVtO84tif1eY4r6zWxVxu3KzIyknJy3rxfRZZg==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-1.4.2.tgz", + "integrity": "sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==", "devOptional": true, "license": "MIT", "peerDependencies": { @@ -8278,9 +8684,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", - "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", "license": "MIT", "engines": { "node": ">=10.0.0" diff --git a/package.json b/package.json index 2d8a239..13e46e3 100644 --- a/package.json +++ b/package.json @@ -20,24 +20,24 @@ "@hookform/resolvers": "^5.2.2", "@iconify/react": "^6.0.2", "@next-auth/prisma-adapter": "^1.0.7", - "@prisma/adapter-pg": "^7.7.0", - "@prisma/client": "^7.7.0", + "@prisma/adapter-pg": "^7.9.1", + "@prisma/client": "^7.9.1", "@supabase/ssr": "^0.10.0", "@supabase/supabase-js": "^2.102.1", "formidable": "^3.5.4", "gsap": "^3.14.2", "lucide-react": "^0.544.0", - "next": "^15.4.6", - "next-auth": "^4.24.13", + "next": "^15.5.23", + "next-auth": "^4.24.15", "pg": "^8.20.0", - "postcss": "^8.5.8", + "postcss": "^8.5.26", "react": "^19.2.4", "react-dom": "^19.2.4", "react-hook-form": "^7.72.0", "sonner": "^2.0.7", "swr": "^2.4.1", "tailwindcss": "^4.2.2", - "uuid": "^13.0.0", + "uuid": "^13.0.2", "zod": "^4.3.6" }, "devDependencies": { @@ -50,9 +50,9 @@ "@types/react-dom": "^19", "autoprefixer": "^10.4.27", "eslint": "^9", - "eslint-config-next": "^15.4.6", + "eslint-config-next": "^15.5.23", "prettier-plugin-tailwindcss": "^0.7.2", - "prisma": "^7.7.0", + "prisma": "^7.9.1", "typescript": "^5" } } diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 01a6047..5b6ab6f 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -13,86 +13,130 @@ datasource db { } model User { - id String @id @default(cuid()) - email String @unique - name String - studentNumber String? @unique @db.VarChar(10) - section String? - role String @default("user") // user, admin, super_admin - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(cuid()) + email String @unique + name String + image String? + studentNumber String? @unique @db.VarChar(10) + section String? + age Int? + dateOfBirth DateTime? + isOldCssMember Boolean? + role String @default("user") // user, admin, super_admin + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt // Relationships - memberApplication MemberApplication? - eaApplication EAApplication? - committeeApplication CommitteeApplication? - ebProfile EBProfile? + memberApplications MemberApplication[] + executiveAssociateApplications ExecutiveAssociateApplication[] + committeeApplications CommitteeApplication[] + ebProfile EBProfile? + memberships Membership[] @@index([role]) } model MemberApplication { - id String @id @default(cuid()) - studentNumber String @unique - user User @relation(fields: [studentNumber], references: [studentNumber], onDelete: Cascade) - paymentProof String - hasAccepted Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - + id String @id @default(cuid()) + studentNumber String + user User @relation(fields: [studentNumber], references: [studentNumber], onDelete: Cascade) + recruitmentCycleId String? + recruitmentCycle RecruitmentCycle? @relation(fields: [recruitmentCycleId], references: [id], onDelete: SetNull) + paymentProof String + hasAccepted Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@unique([studentNumber, recruitmentCycleId]) @@index([hasAccepted]) + @@index([recruitmentCycleId]) +} + +model Membership { + id String @id @default(cuid()) + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + recruitmentCycleId String + recruitmentCycle RecruitmentCycle @relation(fields: [recruitmentCycleId], references: [id], onDelete: Cascade) + memberId String @unique + memberSequence Int + createdAt DateTime @default(now()) + + @@unique([userId, recruitmentCycleId]) + @@unique([recruitmentCycleId, memberSequence]) + @@index([recruitmentCycleId]) + @@index([userId]) } -model EAApplication { - id String @id @default(cuid()) - studentNumber String @unique - user User @relation(fields: [studentNumber], references: [studentNumber], onDelete: Cascade) - ebRole String +model MembershipCounter { + id String @id @default(cuid()) + recruitmentCycleId String @unique + recruitmentCycle RecruitmentCycle @relation(fields: [recruitmentCycleId], references: [id], onDelete: Cascade) + nextSequence Int @default(0) + updatedAt DateTime @updatedAt +} + +model ExecutiveAssociateApplication { + id String @id @default(cuid()) + studentNumber String + user User @relation(fields: [studentNumber], references: [studentNumber], onDelete: Cascade) + recruitmentCycleId String? + recruitmentCycle RecruitmentCycle? @relation(fields: [recruitmentCycleId], references: [id], onDelete: SetNull) + ebRole String firstOptionEb String secondOptionEb String cv String + paymentProof String? supabaseFilePath String? interviewSlotDay String? interviewSlotTimeStart String? interviewSlotTimeEnd String? interviewBy String? - hasFinishedInterview Boolean @default(false) - status String? // passed, failed, redirected + hasFinishedInterview Boolean @default(false) + status String? // passed, failed, redirected redirection String? - hasAccepted Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + hasAccepted Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + @@unique([studentNumber, recruitmentCycleId]) @@index([status]) @@index([interviewBy]) @@index([hasAccepted]) @@index([hasFinishedInterview]) + @@index([recruitmentCycleId]) + @@map("EAApplication") } model CommitteeApplication { - id String @id @default(cuid()) - studentNumber String @unique - user User @relation(fields: [studentNumber], references: [studentNumber], onDelete: Cascade) + id String @id @default(cuid()) + studentNumber String + user User @relation(fields: [studentNumber], references: [studentNumber], onDelete: Cascade) + recruitmentCycleId String? + recruitmentCycle RecruitmentCycle? @relation(fields: [recruitmentCycleId], references: [id], onDelete: SetNull) firstOptionCommittee String secondOptionCommittee String portfolioLink String? cv String + paymentProof String? supabaseFilePath String? interviewSlotDay String? interviewSlotTimeStart String? interviewSlotTimeEnd String? - hasFinishedInterview Boolean @default(false) + hasFinishedInterview Boolean @default(false) interviewBy String? - status String? // passed, failed, redirected + status String? // passed, failed, redirected redirection String? - hasAccepted Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + hasAccepted Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + @@unique([studentNumber, recruitmentCycleId]) @@index([status]) @@index([interviewBy]) @@index([hasAccepted]) @@index([hasFinishedInterview]) + @@index([recruitmentCycleId]) } model AvailableEBInterviewTime { @@ -111,16 +155,20 @@ model AvailableEBInterviewTime { } model EBProfile { - id String @id @default(cuid()) - userId String @unique - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - position String - committees String[] - meetingLink String? - isActive Boolean @default(true) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt - + id String @id @default(cuid()) + userId String @unique + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + recruitmentCycleId String? + recruitmentCycle RecruitmentCycle? @relation(fields: [recruitmentCycleId], references: [id], onDelete: SetNull) + position String + committees String[] + meetingLink String? + isActive Boolean @default(true) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([position]) + @@index([recruitmentCycleId]) @@index([isActive]) } @@ -134,14 +182,20 @@ model SystemConfig { } model RecruitmentCycle { - id String @id @default(cuid()) - schoolYear String @unique // e.g. "2025-2026" - applicationStart DateTime - interviewStart DateTime - interviewEnd DateTime - isActive Boolean @default(false) - createdAt DateTime @default(now()) - updatedAt DateTime @updatedAt + id String @id @default(cuid()) + schoolYear String @unique // e.g. "2025-2026" + applicationStart DateTime + interviewStart DateTime + interviewEnd DateTime + isActive Boolean @default(false) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + ebProfiles EBProfile[] + memberApplications MemberApplication[] + executiveAssociateApplications ExecutiveAssociateApplication[] + committeeApplications CommitteeApplication[] + memberships Membership[] + membershipCounters MembershipCounter[] @@index([isActive]) } diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_ACADEMICS.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_ACADEMICS.webp new file mode 100644 index 0000000..5922d4c Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_ACADEMICS.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_COMMDEV.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_COMMDEV.webp new file mode 100644 index 0000000..d2234ec Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_COMMDEV.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_CREATIVES.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_CREATIVES.webp new file mode 100644 index 0000000..2287a17 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_CREATIVES.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_DOCU.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_DOCU.webp new file mode 100644 index 0000000..e466875 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_DOCU.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_EXTERNALS.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_EXTERNALS.webp new file mode 100644 index 0000000..79c1e7a Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_EXTERNALS.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_FINANCE.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_FINANCE.webp new file mode 100644 index 0000000..31ef60e Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_FINANCE.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_LOGISTICS.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_LOGISTICS.webp new file mode 100644 index 0000000..ed1e999 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_LOGISTICS.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_PUBLICITY.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_PUBLICITY.webp new file mode 100644 index 0000000..4dd485c Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_PUBLICITY.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_SPOTA.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_SPOTA.webp new file mode 100644 index 0000000..848a651 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_SPOTA.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/CSAR_TECHDEV.webp b/public/assets/css-apply-static-images/assets/committee_test/CSAR_TECHDEV.webp new file mode 100644 index 0000000..6cb088b Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/CSAR_TECHDEV.webp differ diff --git a/public/assets/css-apply-static-images/assets/committee_test/Questions CSAR.webp b/public/assets/css-apply-static-images/assets/committee_test/Questions CSAR.webp new file mode 100644 index 0000000..b6b858b Binary files /dev/null and b/public/assets/css-apply-static-images/assets/committee_test/Questions CSAR.webp differ diff --git a/public/assets/css-apply-static-images/assets/logos/Logo_CSS Apply.svg b/public/assets/css-apply-static-images/assets/logos/Logo_CSS Apply.svg new file mode 100644 index 0000000..faf5c96 --- /dev/null +++ b/public/assets/css-apply-static-images/assets/logos/Logo_CSS Apply.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/css-apply-static-images/assets/logos/Logo_CSS.webp b/public/assets/css-apply-static-images/assets/logos/Logo_CSS.webp new file mode 100644 index 0000000..700c015 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/logos/Logo_CSS.webp differ diff --git a/public/assets/css-apply-static-images/assets/logos/csar.webp b/public/assets/css-apply-static-images/assets/logos/csar.webp new file mode 100644 index 0000000..40636a1 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/logos/csar.webp differ diff --git a/public/assets/css-apply-static-images/assets/partners/BiteSlice.webp b/public/assets/css-apply-static-images/assets/partners/BiteSlice.webp new file mode 100644 index 0000000..da8f845 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/partners/BiteSlice.webp differ diff --git a/public/assets/css-apply-static-images/assets/partners/HomeRoom.webp b/public/assets/css-apply-static-images/assets/partners/HomeRoom.webp new file mode 100644 index 0000000..279c9ed Binary files /dev/null and b/public/assets/css-apply-static-images/assets/partners/HomeRoom.webp differ diff --git a/public/assets/css-apply-static-images/assets/partners/MindZone.webp b/public/assets/css-apply-static-images/assets/partners/MindZone.webp new file mode 100644 index 0000000..57fcd43 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/partners/MindZone.webp differ diff --git a/public/assets/css-apply-static-images/assets/partners/NomuCafe.webp b/public/assets/css-apply-static-images/assets/partners/NomuCafe.webp new file mode 100644 index 0000000..6fa9b18 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/partners/NomuCafe.webp differ diff --git a/public/assets/css-apply-static-images/assets/partners/TheCatalyst.webp b/public/assets/css-apply-static-images/assets/partners/TheCatalyst.webp new file mode 100644 index 0000000..31fecaa Binary files /dev/null and b/public/assets/css-apply-static-images/assets/partners/TheCatalyst.webp differ diff --git a/public/assets/css-apply-static-images/assets/partners/Yorokobi.webp b/public/assets/css-apply-static-images/assets/partners/Yorokobi.webp new file mode 100644 index 0000000..1262009 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/partners/Yorokobi.webp differ diff --git a/public/assets/css-apply-static-images/assets/partners/ZeroCafe.webp b/public/assets/css-apply-static-images/assets/partners/ZeroCafe.webp new file mode 100644 index 0000000..ab6c443 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/partners/ZeroCafe.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/CSAR_Excited.webp b/public/assets/css-apply-static-images/assets/pictures/CSAR_Excited.webp new file mode 100644 index 0000000..57ec466 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/CSAR_Excited.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/CSAR_Sad.webp b/public/assets/css-apply-static-images/assets/pictures/CSAR_Sad.webp new file mode 100644 index 0000000..bf260d5 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/CSAR_Sad.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/MemberImage1.webp b/public/assets/css-apply-static-images/assets/pictures/MemberImage1.webp new file mode 100644 index 0000000..cfe9008 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/MemberImage1.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/background.webp b/public/assets/css-apply-static-images/assets/pictures/background.webp new file mode 100644 index 0000000..3e9ced6 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/background.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage1.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage1.webp new file mode 100644 index 0000000..5ee2f9b Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage1.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage10.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage10.webp new file mode 100644 index 0000000..3eb4d1d Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage10.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage11.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage11.webp new file mode 100644 index 0000000..d9caf95 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage11.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage2.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage2.webp new file mode 100644 index 0000000..0e78288 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage2.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage3.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage3.webp new file mode 100644 index 0000000..68dee21 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage3.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage4.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage4.webp new file mode 100644 index 0000000..84929ea Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage4.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage5.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage5.webp new file mode 100644 index 0000000..f44cb16 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage5.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage6.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage6.webp new file mode 100644 index 0000000..e0d26bd Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage6.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage7.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage7.webp new file mode 100644 index 0000000..f38495d Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage7.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage8.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage8.webp new file mode 100644 index 0000000..8e5ebba Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage8.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage9.webp b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage9.webp new file mode 100644 index 0000000..b45c916 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/landingpage/landingpage9.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/loadingscreen_background.webp b/public/assets/css-apply-static-images/assets/pictures/loadingscreen_background.webp new file mode 100644 index 0000000..af97f05 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/loadingscreen_background.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic1.webp b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic1.webp new file mode 100644 index 0000000..ead2350 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic1.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic2.webp b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic2.webp new file mode 100644 index 0000000..fc00b6b Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic2.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic3.webp b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic3.webp new file mode 100644 index 0000000..371283a Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic3.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic4.webp b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic4.webp new file mode 100644 index 0000000..eddfca6 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_desktop_pic4.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic1.webp b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic1.webp new file mode 100644 index 0000000..2bbb8a6 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic1.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic2.webp b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic2.webp new file mode 100644 index 0000000..4b8316d Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic2.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic3.webp b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic3.webp new file mode 100644 index 0000000..6b0886a Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic3.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic4.webp b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic4.webp new file mode 100644 index 0000000..8918954 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/s4_mobile_pic4.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/sec2_pic1.webp b/public/assets/css-apply-static-images/assets/pictures/sec2_pic1.webp new file mode 100644 index 0000000..03a00c8 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/sec2_pic1.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/sec2_pic2.webp b/public/assets/css-apply-static-images/assets/pictures/sec2_pic2.webp new file mode 100644 index 0000000..98a45d9 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/sec2_pic2.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/sec2_pic3.webp b/public/assets/css-apply-static-images/assets/pictures/sec2_pic3.webp new file mode 100644 index 0000000..7218169 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/sec2_pic3.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/sec2_pic4.webp b/public/assets/css-apply-static-images/assets/pictures/sec2_pic4.webp new file mode 100644 index 0000000..362abf3 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/sec2_pic4.webp differ diff --git a/public/assets/css-apply-static-images/assets/pictures/sec2_pic5.webp b/public/assets/css-apply-static-images/assets/pictures/sec2_pic5.webp new file mode 100644 index 0000000..37eab76 Binary files /dev/null and b/public/assets/css-apply-static-images/assets/pictures/sec2_pic5.webp differ diff --git a/public/file.svg b/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/icons/bg-icon-0.svg b/public/icons/bg-icon-0.svg new file mode 100644 index 0000000..6ca2408 --- /dev/null +++ b/public/icons/bg-icon-0.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-1.svg b/public/icons/bg-icon-1.svg new file mode 100644 index 0000000..9698302 --- /dev/null +++ b/public/icons/bg-icon-1.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-10.svg b/public/icons/bg-icon-10.svg new file mode 100644 index 0000000..0951574 --- /dev/null +++ b/public/icons/bg-icon-10.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-11.svg b/public/icons/bg-icon-11.svg new file mode 100644 index 0000000..7dd6bec --- /dev/null +++ b/public/icons/bg-icon-11.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-2.svg b/public/icons/bg-icon-2.svg new file mode 100644 index 0000000..bfd2e41 --- /dev/null +++ b/public/icons/bg-icon-2.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-3.svg b/public/icons/bg-icon-3.svg new file mode 100644 index 0000000..d1f6df1 --- /dev/null +++ b/public/icons/bg-icon-3.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-4.svg b/public/icons/bg-icon-4.svg new file mode 100644 index 0000000..072bd7f --- /dev/null +++ b/public/icons/bg-icon-4.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-5.svg b/public/icons/bg-icon-5.svg new file mode 100644 index 0000000..bea1295 --- /dev/null +++ b/public/icons/bg-icon-5.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-6.svg b/public/icons/bg-icon-6.svg new file mode 100644 index 0000000..aa1e99e --- /dev/null +++ b/public/icons/bg-icon-6.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-7.svg b/public/icons/bg-icon-7.svg new file mode 100644 index 0000000..919eb1a --- /dev/null +++ b/public/icons/bg-icon-7.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-8.svg b/public/icons/bg-icon-8.svg new file mode 100644 index 0000000..ffbf75f --- /dev/null +++ b/public/icons/bg-icon-8.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/bg-icon-9.svg b/public/icons/bg-icon-9.svg new file mode 100644 index 0000000..b5bc189 --- /dev/null +++ b/public/icons/bg-icon-9.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/briefcase.svg b/public/icons/briefcase.svg new file mode 100644 index 0000000..1df1ae9 --- /dev/null +++ b/public/icons/briefcase.svg @@ -0,0 +1,4 @@ + + + + diff --git a/public/icons/calendar.svg b/public/icons/calendar.svg new file mode 100644 index 0000000..7e8425d --- /dev/null +++ b/public/icons/calendar.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/check.svg b/public/icons/check.svg new file mode 100644 index 0000000..8ed2d62 --- /dev/null +++ b/public/icons/check.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/chevron-down-dropdown.svg b/public/icons/chevron-down-dropdown.svg new file mode 100644 index 0000000..c8cd9f5 --- /dev/null +++ b/public/icons/chevron-down-dropdown.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/chevron-down.svg b/public/icons/chevron-down.svg new file mode 100644 index 0000000..2cf3543 --- /dev/null +++ b/public/icons/chevron-down.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/edit.svg b/public/icons/edit.svg new file mode 100644 index 0000000..92ca7d3 --- /dev/null +++ b/public/icons/edit.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/email.svg b/public/icons/email.svg new file mode 100644 index 0000000..037a67d --- /dev/null +++ b/public/icons/email.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/facebook.svg b/public/icons/facebook.svg new file mode 100644 index 0000000..bcd8380 --- /dev/null +++ b/public/icons/facebook.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/file-text.svg b/public/icons/file-text.svg new file mode 100644 index 0000000..acea77d --- /dev/null +++ b/public/icons/file-text.svg @@ -0,0 +1,7 @@ + + + + + + + diff --git a/public/icons/instagram.svg b/public/icons/instagram.svg new file mode 100644 index 0000000..ec8e79c --- /dev/null +++ b/public/icons/instagram.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/logout.svg b/public/icons/logout.svg new file mode 100644 index 0000000..5c2103d --- /dev/null +++ b/public/icons/logout.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/icons/mail.svg b/public/icons/mail.svg new file mode 100644 index 0000000..330be6e --- /dev/null +++ b/public/icons/mail.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/menu.svg b/public/icons/menu.svg new file mode 100644 index 0000000..89614c7 --- /dev/null +++ b/public/icons/menu.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/refresh.svg b/public/icons/refresh.svg new file mode 100644 index 0000000..e08726b --- /dev/null +++ b/public/icons/refresh.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/search.svg b/public/icons/search.svg new file mode 100644 index 0000000..f8f947f --- /dev/null +++ b/public/icons/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/send.svg b/public/icons/send.svg new file mode 100644 index 0000000..9c8c757 --- /dev/null +++ b/public/icons/send.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/star.svg b/public/icons/star.svg new file mode 100644 index 0000000..e049a78 --- /dev/null +++ b/public/icons/star.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/trash.svg b/public/icons/trash.svg new file mode 100644 index 0000000..b3af069 --- /dev/null +++ b/public/icons/trash.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/icons/users.svg b/public/icons/users.svg new file mode 100644 index 0000000..6dec536 --- /dev/null +++ b/public/icons/users.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/icons/warning.svg b/public/icons/warning.svg new file mode 100644 index 0000000..b61bdc7 --- /dev/null +++ b/public/icons/warning.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/window.svg b/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/scripts/send-css-group-reminder.js b/scripts/send-css-group-reminder.js index eba58e9..680a225 100644 --- a/scripts/send-css-group-reminder.js +++ b/scripts/send-css-group-reminder.js @@ -1,15 +1,11 @@ #!/usr/bin/env node /** - * CSS Group Payment Reminder Email Script + * CSS Group and Payment Email Campaign Script * - * This script sends reminder emails to: - * - Accepted member applicants - * - Accepted EA applicants - * - Accepted committee staff applicants - * - Admin and staff users - * - * The email reminds them to pay the fee and join the CSS Group via Facebook. + * This script sends emails to accepted applicants: + * 1. Payment Reminder (for accepted unpaid users) + * 2. CSS Group Invitation (for accepted paid users, checking super admin config) */ const { PrismaClient } = require("@prisma/client"); @@ -25,124 +21,236 @@ apiInstance.setApiKey( process.env.BREVO_API_KEY || "", ); -// CSS Group Facebook link -const CSS_GROUP_LINK = "https://www.facebook.com/groups/1509464253581308"; - -// Helper function to truncate user ID to last 7 characters (matching email.ts) +// Helper function to truncate user ID to last 7 characters const truncateToLast7 = (userId) => { if (!userId) return "UNKNOWN"; return userId.slice(-7); }; -// Email template -const createReminderEmailTemplate = (userName, userId) => { - return ` -
-
-

CSSApply

-

Computer Science Society

-
- -

CSS Group Payment Reminder

- -

- Dear ${userName}, -

- -

- We hope this message finds you well! This is a friendly reminder about your CSS membership and joining our official CSS Group. -

- -
-

✅ Already Paid & Joined?

-

- If you have already completed your membership fee payment and joined our CSS Group, you can safely disregard this email. -

-
- - -
-

💳 Payment Instructions

- -

- To complete your membership, please proceed with the payment of ₱250.00 using the GCash QR code below: -

- -
- GCash QR Code for CSS Payment -
- -
-

⚠️ IMPORTANT PAYMENT MESSAGE

-

- When sending your payment via GCash QR, you MUST include this message: -

-
- Member ID: ${truncateToLast7(userId).toUpperCase()} -
-

- This message is required for payment verification and processing. -

-
- -

- Please keep a screenshot of your payment confirmation for your records. -

-
- -
-

🔗 Join Our CSS Group

-

After payment, join our official CSS Group on Facebook:

-
- - Join CSS Group on Facebook - -
-

- Stay connected with fellow CSS members and get the latest updates! -

-
- -
-

💾 Save Payment Proof

-

Important: Please save your payment proof in your UST Google Drive:

- -
- -
-

📋 Complete Checklist

-

As an accepted member of CSS, please ensure you have:

- -
- -

- If you have any questions or concerns, please don't hesitate to reach out to us. -

- -

- Thank you for being part of the CSS community! -

- -
-

- Best regards,
- CSSApply Team -

-
+// Fetch dynamic payment QR path or return fallback +const getPaymentQrUrl = async () => { + try { + const config = await prisma.systemConfig.findUnique({ + where: { key: "payment_qr_image_path" }, + }); + if (config && config.value && process.env.NEXTAUTH_URL) { + return `${process.env.NEXTAUTH_URL}/api/payment-qr/image?v=${encodeURIComponent(config.value)}`; + } + } catch (error) { + console.error("⚠️ Error fetching dynamic payment QR:", error); + } + // Fallback to static image + return "https://itvimtcxzsubgcbnknvq.supabase.co/storage/v1/object/sign/payment/CSSPayment-Cropped.jpg?token=eyJraWQiOiJzdG9yYWdlLXVybC1zaWduaW5nLWtleV8zZDI2NmE0Mi02NGNmLTQzZjItOTE5Mi00OTk1MmViZDMxY2QiLCJhbGciOiJIUzI1NiJ9.eyJ1cmwiOiJwYXltZW50L0NTU1BheW1lbnQtQ3JvcHBlZC5qcGciLCJpYXQiOjE3NTk1ODE4MjksImV4cCI6MTc5MTExNzgyOX0.SVFyO2WgwnA0pasjevIYWNESH6udyOLJiivdGob-FP4"; +}; + +// Fetch dynamic community group config or return fallbacks +const getCommunityGroupConfig = async () => { + try { + const configs = await prisma.systemConfig.findMany({ + where: { + key: { + in: ["community_group_url", "community_group_label", "community_group_enabled"], + }, + }, + }); + + const configMap = new Map(configs.map((c) => [c.key, c.value])); + return { + enabled: configMap.get("community_group_enabled") !== "false", + url: configMap.get("community_group_url")?.trim() || "https://www.facebook.com/groups/1509464253581308", + label: configMap.get("community_group_label")?.trim() || "Join UST CSS Members 25'-26' Group", + }; + } catch (error) { + console.error("⚠️ Error fetching community group config:", error); + return { + enabled: true, + url: "https://www.facebook.com/groups/1509464253581308", + label: "Join UST CSS Members 25'-26' Group", + }; + } +}; + +// Reusable standard email layout wrapper with premium CSS theme +const wrapScriptEmail = (title, innerHtml) => ` + + + + + + + + + + + +
+ + + +
+ + +`; + +// Payment Reminder Email Template +const createPaymentReminderTemplate = (userName) => { + return wrapScriptEmail( + "CSS Membership Payment Reminder", + ` +

Dear ${userName},

+

We hope this message finds you well. This is a friendly reminder to complete your CSS membership payment.

+ +
+

Payment Instructions

+

To complete your membership, please log in to the CSSApply recruitment portal to view the GCash QR code, download the acknowledgement receipt, and submit your payment proof.

+
+ +
+ + Log In to CSSApply + +
+ +

After paying, please upload your receipt proof to your UST Google Drive, generate a shareable link, and submit it on your application dashboard page to claim your permanent Member ID.

+

Thank you for being part of the CSS community.

+ ` + ); +}; + +// CSS Group Invitation Email Template +const createCssGroupInvitationTemplate = (userName, groupUrl, groupLabel) => { + return wrapScriptEmail( + "Join the CSS Community Group", + ` +

Dear ${userName},

+

Congratulations. We have verified your membership payment. You are now officially a member of the Computer Science Society.

+

As a next step, we would like to invite you to join our official community group where we post updates, events, and announcements.

+ +
+

CSS Community Group

+

Click the button below to join the official CSS group:

+
+ + ${groupLabel} + +
+

+ Please make sure to answer the membership questions when requesting to join. +

- `; + +

Welcome once again, and we look forward to seeing you in the group and at our upcoming activities.

+ ` + ); }; // Send email function @@ -153,15 +261,12 @@ const sendEmail = async (to, subject, html) => { sendSmtpEmail.subject = subject; sendSmtpEmail.htmlContent = html; sendSmtpEmail.sender = { - name: "CSS Apply", + name: "CSSApply", email: process.env.BREVO_FROM_EMAIL || "noreply@cssapply.com", }; sendSmtpEmail.to = [{ email: to }]; const result = await apiInstance.sendTransacEmail(sendSmtpEmail); - console.log( - `✅ Email sent successfully to ${to}: ${result.body?.messageId}`, - ); return { success: true, messageId: result.body?.messageId }; } catch (error) { console.error(`❌ Error sending email to ${to}:`, error); @@ -169,278 +274,292 @@ const sendEmail = async (to, subject, html) => { } }; -// Get all target users (only accepted applications) -const getTargetUsers = async () => { - console.log("🔍 Fetching target users (accepted applications only)..."); +// Get the active recruitment cycle ID +const getActiveCycleId = async () => { + const activeCycle = await prisma.recruitmentCycle.findFirst({ + where: { isActive: true }, + select: { id: true } + }); + return activeCycle ? activeCycle.id : null; +}; - try { - // Get users with accepted member applications - const acceptedMembers = await prisma.user.findMany({ - where: { - memberApplication: { - hasAccepted: true, - }, - }, - select: { - id: true, - name: true, - email: true, - studentNumber: true, - }, - }); +// Get all unpaid accepted applicants +const getUnpaidUsers = async () => { + console.log("🔍 Fetching accepted unpaid users..."); + const cycleId = await getActiveCycleId(); + if (!cycleId) { + console.log("⚠️ No active recruitment cycle found!"); + return []; + } - // Get users with accepted EA applications - const acceptedEAs = await prisma.user.findMany({ - where: { - eaApplication: { - hasAccepted: true, - }, - }, - select: { - id: true, - name: true, - email: true, - studentNumber: true, - }, - }); + const members = await prisma.memberApplication.findMany({ + where: { + hasAccepted: true, + recruitmentCycleId: cycleId, + paymentProof: "", + }, + include: { user: true }, + }); - // Get users with accepted committee applications - const acceptedCommitteeStaff = await prisma.user.findMany({ - where: { - committeeApplication: { - hasAccepted: true, - }, - }, - select: { - id: true, - name: true, - email: true, - studentNumber: true, - }, - }); + const eas = await prisma.eAApplication.findMany({ + where: { + hasAccepted: true, + recruitmentCycleId: cycleId, + OR: [ + { paymentProof: null }, + { paymentProof: "" } + ] + }, + include: { user: true }, + }); - // Combine all users and remove duplicates - const allUsers = [ - ...acceptedMembers, - ...acceptedEAs, - ...acceptedCommitteeStaff, - ]; - - // Remove duplicates based on email - const uniqueUsers = allUsers.filter( - (user, index, self) => - index === self.findIndex((u) => u.email === user.email), - ); - - console.log(`📊 Found ${uniqueUsers.length} unique target users:`); - console.log(` - Accepted Members: ${acceptedMembers.length}`); - console.log(` - Accepted EAs: ${acceptedEAs.length}`); - console.log( - ` - Accepted Committee Staff: ${acceptedCommitteeStaff.length}`, - ); - - return uniqueUsers; - } catch (error) { - console.error("❌ Error fetching target users:", error); - throw error; + const staffs = await prisma.committeeApplication.findMany({ + where: { + hasAccepted: true, + recruitmentCycleId: cycleId, + OR: [ + { paymentProof: null }, + { paymentProof: "" } + ] + }, + include: { user: true }, + }); + + const allUnpaid = []; + const emails = new Set(); + + for (const app of [...members, ...eas, ...staffs]) { + if (app.user && !emails.has(app.user.email)) { + emails.add(app.user.email); + allUnpaid.push({ + id: app.user.id, + name: app.user.name, + email: app.user.email, + studentNumber: app.studentNumber || app.user.studentNumber, + }); + } } + + console.log(`📊 Found ${allUnpaid.length} unique unpaid users:`); + console.log(` - Unpaid Members: ${members.length}`); + console.log(` - Unpaid EAs: ${eas.length}`); + console.log(` - Unpaid Staffs: ${staffs.length}`); + + return allUnpaid; }; -// Main function to send reminder emails -const sendReminderEmails = async () => { - console.log("🚀 Starting CSS Group payment reminder email campaign..."); - console.log(`📧 CSS Group Link: ${CSS_GROUP_LINK}`); +// Get all paid accepted applicants +const getPaidUsers = async () => { + console.log("🔍 Fetching accepted paid users..."); + const cycleId = await getActiveCycleId(); + if (!cycleId) { + console.log("⚠️ No active recruitment cycle found!"); + return []; + } - try { - // Get target users - const targetUsers = await getTargetUsers(); + const members = await prisma.memberApplication.findMany({ + where: { + hasAccepted: true, + recruitmentCycleId: cycleId, + paymentProof: { not: "" }, + }, + include: { user: true }, + }); - if (targetUsers.length === 0) { - console.log("⚠️ No target users found. Exiting."); - return; + const eas = await prisma.eAApplication.findMany({ + where: { + hasAccepted: true, + recruitmentCycleId: cycleId, + paymentProof: { notIn: [null, ""] }, + }, + include: { user: true }, + }); + + const staffs = await prisma.committeeApplication.findMany({ + where: { + hasAccepted: true, + recruitmentCycleId: cycleId, + paymentProof: { notIn: [null, ""] }, + }, + include: { user: true }, + }); + + const allPaid = []; + const emails = new Set(); + + for (const app of [...members, ...eas, ...staffs]) { + if (app.user && !emails.has(app.user.email)) { + emails.add(app.user.email); + allPaid.push({ + id: app.user.id, + name: app.user.name, + email: app.user.email, + studentNumber: app.studentNumber || app.user.studentNumber, + }); } + } + + console.log(`📊 Found ${allPaid.length} unique paid users:`); + console.log(` - Paid Members: ${members.length}`); + console.log(` - Paid EAs: ${eas.length}`); + console.log(` - Paid Staffs: ${staffs.length}`); + + return allPaid; +}; + +// Execute Payment Reminder Email Campaign +const runPaymentReminderCampaign = async () => { + console.log("\n🚀 Starting Payment Reminder Campaign..."); + const targetUsers = await getUnpaidUsers(); + if (targetUsers.length === 0) { + console.log("⚠️ No unpaid target users found. Campaign skipped."); + return; + } - // Email configuration - const subject = "CSS Group Payment Reminder - Join Our Official Group"; - let successCount = 0; - let failureCount = 0; - const failures = []; - - console.log(`📤 Sending emails to ${targetUsers.length} users...`); - - // Send emails to each user - for (const user of targetUsers) { - try { - const html = createReminderEmailTemplate( - user.name || "Valued Member", - user.id, - ); - const result = await sendEmail(user.email, subject, html); - - if (result.success) { - successCount++; - console.log(`✅ Sent to ${user.name} (${user.email})`); - } else { - failureCount++; - failures.push({ - user: user.name, - email: user.email, - error: result.error, - }); - console.log(`❌ Failed to send to ${user.name} (${user.email})`); - } - - // Add a small delay to avoid rate limiting - await new Promise((resolve) => setTimeout(resolve, 100)); - } catch (error) { + let successCount = 0; + let failureCount = 0; + const failures = []; + + for (const user of targetUsers) { + try { + const html = createPaymentReminderTemplate( + user.name || "Valued Member" + ); + const result = await sendEmail(user.email, "CSS Group Payment Reminder", html); + + if (result.success) { + successCount++; + console.log(` ✅ Sent to ${user.name} (${user.email})`); + } else { failureCount++; - failures.push({ - user: user.name, - email: user.email, - error: error.message, - }); - console.error( - `❌ Error processing ${user.name} (${user.email}):`, - error, - ); + failures.push({ user: user.name, email: user.email, error: result.error }); + console.log(` ❌ Failed to send to ${user.name} (${user.email})`); } - } - // Summary - console.log("\n📊 Email Campaign Summary:"); - console.log(`✅ Successfully sent: ${successCount}`); - console.log(`❌ Failed to send: ${failureCount}`); - console.log(`📧 Total recipients: ${targetUsers.length}`); - - if (failures.length > 0) { - console.log("\n❌ Failed emails:"); - failures.forEach((failure) => { - console.log( - ` - ${failure.user} (${failure.email}): ${failure.error}`, - ); - }); + await new Promise((resolve) => setTimeout(resolve, 100)); + } catch (error) { + failureCount++; + failures.push({ user: user.name, email: user.email, error: error.message }); + console.error(` ❌ Error processing ${user.name} (${user.email}):`, error); } + } - console.log("\n🎉 CSS Group reminder email campaign completed!"); - } catch (error) { - console.error("❌ Campaign failed:", error); - process.exit(1); - } finally { - await prisma.$disconnect(); + console.log("\n📊 Payment Reminder Campaign Summary:"); + console.log(`✅ Successfully sent: ${successCount}`); + console.log(`❌ Failed to send: ${failureCount}`); + if (failures.length > 0) { + console.log("❌ Failures detail:"); + failures.forEach((f) => console.log(` - ${f.user} (${f.email}): ${f.error}`)); } }; -// Check environment variables -const checkEnvironment = () => { - const requiredEnvVars = ["BREVO_API_KEY", "BREVO_FROM_EMAIL", "DATABASE_URL"]; - const missing = requiredEnvVars.filter((envVar) => !process.env[envVar]); +// Execute CSS Group Invitation Email Campaign +const runCssGroupInvitationCampaign = async () => { + console.log("\n🚀 Starting CSS Group Invitation Campaign..."); + const groupConfig = await getCommunityGroupConfig(); - if (missing.length > 0) { - console.error("❌ Missing required environment variables:"); - missing.forEach((envVar) => console.error(` - ${envVar}`)); - console.error("\nPlease set these environment variables and try again."); - process.exit(1); + if (!groupConfig.enabled) { + console.log("⚠️ CSS Group Invitation is currently DISABLED in super admin settings. Campaign aborted."); + return; } - console.log("✅ Environment variables validated"); -}; + console.log(`🔗 Target FB Group URL: ${groupConfig.url}`); + console.log(`🏷️ Button Label: ${groupConfig.label}`); -// Get specific user by email -const getSpecificUser = async (email) => { - console.log(`🔍 Looking up user: ${email}`); + const targetUsers = await getPaidUsers(); + if (targetUsers.length === 0) { + console.log("⚠️ No paid target users found. Campaign skipped."); + return; + } - try { - const user = await prisma.user.findUnique({ - where: { email: email }, - select: { - id: true, - name: true, - email: true, - studentNumber: true, - role: true, - memberApplication: { - select: { hasAccepted: true }, - }, - eaApplication: { - select: { hasAccepted: true }, - }, - committeeApplication: { - select: { hasAccepted: true }, - }, - ebProfile: { - select: { position: true, isActive: true }, - }, - }, - }); + let successCount = 0; + let failureCount = 0; + const failures = []; + + for (const user of targetUsers) { + try { + const html = createCssGroupInvitationTemplate( + user.name || "Valued Member", + groupConfig.url, + groupConfig.label + ); + const result = await sendEmail(user.email, "Join the CSS Community Group", html); + + if (result.success) { + successCount++; + console.log(` ✅ Sent to ${user.name} (${user.email})`); + } else { + failureCount++; + failures.push({ user: user.name, email: user.email, error: result.error }); + console.log(` ❌ Failed to send to ${user.name} (${user.email})`); + } - if (!user) { - console.log(`❌ User not found: ${email}`); - return null; + await new Promise((resolve) => setTimeout(resolve, 100)); + } catch (error) { + failureCount++; + failures.push({ user: user.name, email: user.email, error: error.message }); + console.error(` ❌ Error processing ${user.name} (${user.email}):`, error); } + } - console.log(`✅ Found user: ${user.name} (${user.email})`); - console.log(` - Role: ${user.role}`); - console.log(` - Student Number: ${user.studentNumber || "N/A"}`); - console.log( - ` - Member Application Accepted: ${user.memberApplication?.hasAccepted || false}`, - ); - console.log( - ` - EA Application Accepted: ${user.eaApplication?.hasAccepted || false}`, - ); - console.log( - ` - Committee Application Accepted: ${user.committeeApplication?.hasAccepted || false}`, - ); - console.log( - ` - EB Profile: ${user.ebProfile ? `${user.ebProfile.position} (Active: ${user.ebProfile.isActive})` : "None"}`, - ); - - return user; - } catch (error) { - console.error(`❌ Error looking up user ${email}:`, error); - return null; + console.log("\n📊 CSS Group Invitation Campaign Summary:"); + console.log(`✅ Successfully sent: ${successCount}`); + console.log(`❌ Failed to send: ${failureCount}`); + if (failures.length > 0) { + console.log("❌ Failures detail:"); + failures.forEach((f) => console.log(` - ${f.user} (${f.email}): ${f.error}`)); } }; -// Send test email to specific user -const sendTestEmail = async (userEmail) => { - console.log("🧪 Sending test email to specific user..."); - +// Send single test email +const sendTestEmail = async (email, type) => { + console.log(`\n🧪 Sending test ${type} to ${email}...`); try { - const user = await getSpecificUser(userEmail); - - if (!user) { - console.log("❌ Cannot send test email - user not found"); - return; - } + const user = await prisma.user.findUnique({ + where: { email }, + select: { id: true, name: true, email: true }, + }); - const subject = "CSS Group Payment Reminder - TEST EMAIL"; - const html = createReminderEmailTemplate( - user.name || "Valued Member", - user.id, - ); + const testId = user ? user.id : "test_user_id"; + const testName = user ? user.name : "Test Recipient"; - console.log(`📤 Sending test email to ${user.name} (${user.email})...`); - const result = await sendEmail(user.email, subject, html); + let subject, html; + if (type === "payment_reminder") { + subject = "CSS Group Payment Reminder [TEST]"; + html = createPaymentReminderTemplate(testName); + } else { + const groupConfig = await getCommunityGroupConfig(); + subject = "CSS Community Group Invitation [TEST]"; + html = createCssGroupInvitationTemplate(testName, groupConfig.url, groupConfig.label); + } + const result = await sendEmail(email, subject, html); if (result.success) { - console.log( - `✅ Test email sent successfully to ${user.name} (${user.email})`, - ); - console.log(`📧 Message ID: ${result.messageId}`); + console.log(`✅ Test email sent successfully to ${email} (Msg ID: ${result.messageId})`); } else { - console.log( - `❌ Failed to send test email to ${user.name} (${user.email})`, - ); - console.log(`❌ Error: ${result.error}`); + console.log(`❌ Failed to send test email: ${result.error}`); } } catch (error) { console.error("❌ Test email failed:", error); - } finally { - await prisma.$disconnect(); } }; -// Interactive prompt function +// Check environment variables +const checkEnvironment = () => { + const requiredEnvVars = ["BREVO_API_KEY", "BREVO_FROM_EMAIL", "DATABASE_URL"]; + const missing = requiredEnvVars.filter((envVar) => !process.env[envVar]); + + if (missing.length > 0) { + console.error("❌ Missing required environment variables:"); + missing.forEach((envVar) => console.error(` - ${envVar}`)); + console.error("\nPlease set these environment variables and try again."); + process.exit(1); + } + + console.log("✅ Environment variables validated"); +}; + +// Interactive menu options const promptUser = () => { return new Promise((resolve) => { const readline = require("readline"); @@ -449,20 +568,22 @@ const promptUser = () => { output: process.stdout, }); - console.log("\n📧 Email Campaign Options:"); - console.log("1 - Send to ALL target users"); - console.log("2 - Send TEST email to joevannipaulo.gumban.cics@ust.edu.ph"); - console.log("3 - Send TEST email to custom email address"); - console.log("4 - Exit"); + console.log("\n📧 Email Campaign Campaigns & Options:"); + console.log("1 - Run Payment Reminders campaign (unpaid users)"); + console.log("2 - Run CSS Group Invitations campaign (paid users)"); + console.log("3 - Send TEST Payment Reminder to joevannipaulo.gumban.cics@ust.edu.ph"); + console.log("4 - Send TEST CSS Group Invitation to joevannipaulo.gumban.cics@ust.edu.ph"); + console.log("5 - Send TEST Payment Reminder to custom email address"); + console.log("6 - Send TEST CSS Group Invitation to custom email address"); + console.log("7 - Exit"); - rl.question("\nPlease select an option (1-4): ", (answer) => { + rl.question("\nPlease select an option (1-7): ", (answer) => { rl.close(); resolve(answer.trim()); }); }); }; -// Custom email prompt const promptCustomEmail = () => { return new Promise((resolve) => { const readline = require("readline"); @@ -478,66 +599,74 @@ const promptCustomEmail = () => { }); }; -// Main execution const main = async () => { - console.log("🎯 CSS Group Payment Reminder Email Script"); - console.log("==========================================\n"); + console.log("🎯 CSS Recruitment Campaigns Manager"); + console.log("====================================\n"); - // Check environment checkEnvironment(); - // Interactive menu const choice = await promptUser(); switch (choice) { case "1": - console.log("\n🚀 Sending to ALL target users..."); - await sendReminderEmails(); + await runPaymentReminderCampaign(); break; case "2": - console.log("\n🧪 Sending TEST email to specified user..."); - await sendTestEmail("joevannipaulo.gumban.cics@ust.edu.ph"); + await runCssGroupInvitationCampaign(); break; case "3": - const customEmail = await promptCustomEmail(); - if (customEmail) { - console.log(`\n🧪 Sending TEST email to: ${customEmail}`); - await sendTestEmail(customEmail); + await sendTestEmail("joevannipaulo.gumban.cics@ust.edu.ph", "payment_reminder"); + break; + + case "4": + await sendTestEmail("joevannipaulo.gumban.cics@ust.edu.ph", "css_group_join"); + break; + + case "5": + const emailReminder = await promptCustomEmail(); + if (emailReminder) { + await sendTestEmail(emailReminder, "payment_reminder"); } else { console.log("❌ No email address provided"); - process.exit(1); } break; - case "4": + case "6": + const emailInvitation = await promptCustomEmail(); + if (emailInvitation) { + await sendTestEmail(emailInvitation, "css_group_join"); + } else { + console.log("❌ No email address provided"); + } + break; + + case "7": console.log("👋 Goodbye!"); - await prisma.$disconnect(); - process.exit(0); break; default: - console.log("❌ Invalid option. Please run the script again."); - await prisma.$disconnect(); - process.exit(1); + console.log("❌ Invalid option."); + break; } + + await prisma.$disconnect(); + process.exit(0); }; // Handle script termination process.on("SIGINT", async () => { - console.log("\n⚠️ Script interrupted. Cleaning up..."); await prisma.$disconnect(); process.exit(0); }); process.on("SIGTERM", async () => { - console.log("\n⚠️ Script terminated. Cleaning up..."); await prisma.$disconnect(); process.exit(0); }); -// Run the script +// Run script if (require.main === module) { main().catch((error) => { console.error("❌ Script failed:", error); @@ -546,8 +675,9 @@ if (require.main === module) { } module.exports = { - sendReminderEmails, - getTargetUsers, - createReminderEmailTemplate, + getUnpaidUsers, + getPaidUsers, + createPaymentReminderTemplate, + createCssGroupInvitationTemplate, sendEmail, }; diff --git a/src/app/admin/applications/page.tsx b/src/app/admin/applications/page.tsx index 2d954ca..1448f15 100644 --- a/src/app/admin/applications/page.tsx +++ b/src/app/admin/applications/page.tsx @@ -6,7 +6,7 @@ import MobileSidebar from "@/components/AdminMobileSB"; import SidebarContent from "@/components/AdminSidebar"; import { committeeRolesSubmitted } from "@/data/committeeRoles"; import { roles } from "@/data/ebRoles"; -import { truncateToLast7 } from "@/lib/truncate-utils"; + import { LucideChevronDown, LucideChevronUp } from "lucide-react"; import { toast } from "sonner"; @@ -89,6 +89,9 @@ const getEARedirectionMessage = (redirection: string): string => { // Default case (EA to EA) return "EA Applicant Redirected"; }; +const getApplicationMemberId = (application: Application) => + application.user.memberships?.[0]?.memberId ?? + application.user.id.slice(-7).toUpperCase(); // Helper function to get EB role full name const getEBRoleFullName = (roleId: string): string => { @@ -112,6 +115,7 @@ interface Application { email: string; studentNumber: string; section: string; + memberships?: Array<{ memberId: string }>; }; hasAccepted?: boolean; status?: string; @@ -128,7 +132,7 @@ interface Application { cvDownloadUrl?: string; portfolioDownloadUrl?: string; createdAt: string; - type: "committee" | "ea" | "member"; + type: "committee" | "executive-associate" | "member"; cv?: string; paymentProof?: string; isAssigned?: boolean; @@ -136,7 +140,7 @@ interface Application { // Cache for EB data to prevent unnecessary API calls const ebDataCache = new Map(); -const CACHE_DURATION = 5 * 60 * 1000; // 5 minutes +const CACHE_DURATION = 0; const ITEMS_PER_PAGE = 10; const Applications = () => { @@ -146,7 +150,7 @@ const Applications = () => { ea: Application[]; member: Application[]; }>({ committee: [], ea: [], member: [] }); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); const [processingId, setProcessingId] = useState(null); const [showRedirectModal, setShowRedirectModal] = useState(false); const [selectedApplication, setSelectedApplication] = @@ -190,6 +194,7 @@ const Applications = () => { } } + setLoading(true); try { // Add cache-busting timestamp const timestamp = Date.now(); @@ -208,6 +213,7 @@ const Applications = () => { return ebProfile; } catch (error) { console.error("Error fetching EB data:", error); + setLoading(false); return null; } }, []); @@ -295,13 +301,19 @@ const Applications = () => { useEffect(() => { if (status === "loading") return; - if (!isInitialized && session?.user?.dbId) { - setIsInitialized(true); - getEBData(session.user.dbId).then((ebProfile) => { - if (ebProfile?.position) { - fetchApplications(ebProfile.position); - } - }); + if (!isInitialized) { + if (session?.user?.dbId) { + setIsInitialized(true); + getEBData(session.user.dbId).then((ebProfile) => { + if (ebProfile?.position) { + fetchApplications(ebProfile.position); + } else { + setLoading(false); + } + }); + } else { + setLoading(false); + } } }, [ status, @@ -315,7 +327,7 @@ const Applications = () => { const handleApplicationAction = useCallback( async ( applicationId: string, - type: "committee" | "ea" | "member", + type: "committee" | "executive-associate" | "member", action: "accept" | "reject" | "redirect" | "evaluate", ) => { if (type === "member" && action === "evaluate") { @@ -388,7 +400,8 @@ const Applications = () => { } if ( - (application.type === "committee" || application.type === "ea") && + (application.type === "committee" || + application.type === "executive-associate") && (!application.interviewSlotDay || !application.interviewSlotTimeStart) ) { return ( @@ -522,7 +535,12 @@ const Applications = () => { setMemberPage(1); setCommitteePage(1); setEaPage(1); - }, [searchQuery, applicationCounts.member, applicationCounts.committee, applicationCounts.ea]); + }, [ + searchQuery, + applicationCounts.member, + applicationCounts.committee, + applicationCounts.ea, + ]); const memberTotalPages = Math.max( 1, @@ -539,7 +557,10 @@ const Applications = () => { const paginatedMemberApplications = useMemo(() => { const startIndex = (memberPage - 1) * ITEMS_PER_PAGE; - return currentApplications.member.slice(startIndex, startIndex + ITEMS_PER_PAGE); + return currentApplications.member.slice( + startIndex, + startIndex + ITEMS_PER_PAGE, + ); }, [currentApplications.member, memberPage]); const paginatedCommitteeApplications = useMemo(() => { @@ -552,13 +573,16 @@ const Applications = () => { const paginatedEaApplications = useMemo(() => { const startIndex = (eaPage - 1) * ITEMS_PER_PAGE; - return currentApplications.ea.slice(startIndex, startIndex + ITEMS_PER_PAGE); + return currentApplications.ea.slice( + startIndex, + startIndex + ITEMS_PER_PAGE, + ); }, [currentApplications.ea, eaPage]); // Show loading for session only (not data fetching) if (status === "loading") { return ( -
+

Loading session...

@@ -568,7 +592,7 @@ const Applications = () => { } return ( -
+
{/* Sidebar Navigation */} @@ -590,6 +614,8 @@ const Applications = () => { getEBData(session.user.id, true).then((freshEbData) => { if (freshEbData?.position) { fetchApplications(freshEbData.position); + } else { + setLoading(false); } }); } @@ -605,6 +631,8 @@ const Applications = () => { getEBData(session.user.id, true).then((freshEbData) => { if (freshEbData?.position) { fetchApplications(freshEbData.position); + } else { + setLoading(false); } }); } @@ -617,16 +645,18 @@ const Applications = () => {

- Review and manage all applications from students for CSS Apply + Review and manage all applications from students for CSSApply

- {ebData?.position && ( + {loading || !ebData ? ( +
+ ) : ebData?.position ? (

Current Position:{" "} {ebData.position}

- )} + ) : null} {/* SEARCH BAR */}
@@ -639,19 +669,16 @@ const Applications = () => { className="w-full px-4 py-2 pl-10 pr-4 text-sm border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-[#044FAF] focus:border-transparent" />
- - - +
{isSearching && (
@@ -812,10 +839,7 @@ const Applications = () => { )} {application.hasAccepted && (
- Member ID:{" "} - {truncateToLast7( - application.user.id, - ).toUpperCase()} + Member ID: {getApplicationMemberId(application)}
)}
@@ -1091,10 +1115,7 @@ const Applications = () => { )} {application.hasAccepted && (
- Member ID:{" "} - {truncateToLast7( - application.user.id, - ).toUpperCase()} + Member ID: {getApplicationMemberId(application)} {application.redirection ? (
Redirected to:{" "} @@ -1129,7 +1150,7 @@ const Applications = () => {

- Executive Assistant Applications + Executive Associate Applications

({applicationCounts.ea}) @@ -1306,7 +1327,7 @@ const Applications = () => { onClick={() => handleApplicationAction( application.id, - "ea", + "executive-associate", "evaluate", ) } @@ -1327,7 +1348,7 @@ const Applications = () => { onClick={() => handleApplicationAction( application.id, - "ea", + "executive-associate", "accept", ) } @@ -1340,7 +1361,7 @@ const Applications = () => { onClick={() => handleApplicationAction( application.id, - "ea", + "executive-associate", "reject", ) } @@ -1363,10 +1384,7 @@ const Applications = () => { )} {application.hasAccepted && (
- Member ID:{" "} - {truncateToLast7( - application.user.id, - ).toUpperCase()} + Member ID: {getApplicationMemberId(application)} {application.redirection ? (
Redirected to:{" "} @@ -1417,7 +1435,7 @@ const Applications = () => { - + {roles.map((role) => ( - ) : selectedApplication.type === "ea" ? ( + ) : selectedApplication.type === "executive-associate" ? ( <> - + {roles.map((role) => (