diff --git a/.gitignore b/.gitignore index 2a62c48..c647ed2 100644 --- a/.gitignore +++ b/.gitignore @@ -22,6 +22,10 @@ next-env.d.ts .env.test.local .env.production.local +# Local dev stack secrets +dev/auth.env +dev/keys/ + # Debug logs npm-debug.log* yarn-debug.log* diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..f439f39 --- /dev/null +++ b/Makefile @@ -0,0 +1,31 @@ +COMPOSE := docker compose -f docker-compose.dev.yml + +.PHONY: dev-setup dev dev-up dev-down dev-logs dev-reset dev-pull + +## One-time: generate dev JWT keys + scaffold env files +dev-setup: + @bash dev/setup.sh + +## Bring up the full local backend stack, then run the frontend (npm run dev) +dev: dev-up + npm run dev + +## Backend stack only (Postgres, Redis, migrate, auth, scraper, calendar, gateway) +dev-up: + $(COMPOSE) up -d --wait + +## Pull the latest :dev backend images +dev-pull: + $(COMPOSE) pull + +## Stop the stack (keeps data) +dev-down: + $(COMPOSE) down + +## Tail logs of the backend stack +dev-logs: + $(COMPOSE) logs -f + +## Nuke everything including the database + scraped data +dev-reset: + $(COMPOSE) down -v diff --git a/dev/Caddyfile b/dev/Caddyfile new file mode 100644 index 0000000..1bbb946 --- /dev/null +++ b/dev/Caddyfile @@ -0,0 +1,32 @@ +# UniPlanner local gateway — path routing only (mirrors prod Traefik). +# Identity headers (X-User-*) are injected by the Next.js dev proxy, not here, +# because auth here is optional (anonymous + public routes must pass through). + +{ + admin off + auto_https off +} + +:8080 { + # Auth service — OAuth flow + user profile/sessions + @auth path /api/v1/auth* /api/v1/users* + handle @auth { + reverse_proxy auth-service:8081 + } + + # Scraper service — courses, subjects, preview, timetable (public) + @scraper path /api/v1/courses* /api/v1/timetable* /api/v1/preview* /api/v1/scraper* + handle @scraper { + reverse_proxy scraper-service:8083 + } + + # Calendar manager — calendars API + public .ics feed + @calendar path /api/v1/calendars* /cal/* + handle @calendar { + reverse_proxy calendar-manager:8082 + } + + handle { + respond "UniPlanner local gateway — unknown route" 404 + } +} diff --git a/dev/README.md b/dev/README.md new file mode 100644 index 0000000..cca17c6 --- /dev/null +++ b/dev/README.md @@ -0,0 +1,74 @@ +# Local dev environment + +Runs the whole UniPlanner stack locally: Postgres + Redis + migrations + the three +backend services (`:dev` images from GHCR) behind a Caddy gateway, with the Next.js +frontend on the host. Google sign-in works locally; Apple is intentionally not wired. + +## Topology + +``` +browser ─▶ frontend (host :3000) + │ /api/v1/* proxy (adds Bearer + X-User-* in dev) + ▼ + gateway Caddy :8080 ──┬─▶ auth-service :8081 /api/v1/auth,/users + ├─▶ scraper-service :8083 /api/v1/courses,preview,timetable + └─▶ calendar-manager :8082 /api/v1/calendars, /cal/*.ics + │ + Postgres :5432 ─┴─ Redis :6379 +``` + +- **Migrations** run as a prod-like pre-hook: `db-clone` fresh-clones the `databases` + repo, `migrate` applies `up`, and no backend starts until it finishes. +- **Identity**: in prod Istio validates the JWT cookie and injects `X-User-Id/X-Email/X-Roles`. + Locally there is no Istio, so the frontend `/api/v1/[...]` proxy decodes the `access_token` + cookie and injects those headers **in dev only**. Caddy is a pure path router. + +## First-time setup + +1. **Docker** running, plus this repo's `npm install` done. + +2. **Generate keys + env scaffolding:** + ```bash + make dev-setup + ``` + This creates a dev JWT keypair, `dev/auth.env`, and `.env.development.local` + (frontend dev overrides — only used by `next dev`, your prod `.env.local` is untouched). + +3. **Create a LOCAL Google OAuth app** (separate from prod so nothing conflicts): + - Google Cloud Console → *APIs & Services* → *Credentials* → *Create OAuth client ID* → *Web application* + - **Authorized JavaScript origins:** `http://localhost:3000` + - **Authorized redirect URIs:** `http://localhost:3000/api/auth/callback` + (Google redirects straight to the frontend callback page, which exchanges the + code via `/api/v1/auth/exchange` and sets the cookies.) + - Put the client id/secret into `dev/auth.env`: + ``` + GOOGLE_CLIENT_ID=...apps.googleusercontent.com + GOOGLE_CLIENT_SECRET=... + ``` + +4. **Go:** + ```bash + make dev + ``` + Backend stack comes up (first boot the scraper warms the UniBo course cache — can take a + few minutes), then the frontend starts on http://localhost:3000. + +## Commands + +| Command | What | +|---|---| +| `make dev-setup` | one-time keys + env scaffolding | +| `make dev` | backend stack (`--wait`) + `npm run dev` | +| `make dev-up` | backend stack only | +| `make dev-pull` | pull latest `:dev` images | +| `make dev-logs` | tail backend logs | +| `make dev-down` | stop (keeps DB + scraped data) | +| `make dev-reset` | **wipe everything** incl. DB volumes | + +## Notes + +- All API goes through the gateway at `http://localhost:8080`; you can curl it directly + (e.g. `curl 'http://localhost:8080/api/v1/courses?q=informatica&lang=it'`). +- Cookies are host-only, non-secure (`COOKIE_SECURE=false`, no `COOKIE_DOMAIN`) so they work on `localhost`. +- Apple sign-in is not available locally (Apple requires a public HTTPS redirect). +- `dev/auth.env` and `dev/keys/` hold secrets and are gitignored. diff --git a/dev/auth.env.example b/dev/auth.env.example new file mode 100644 index 0000000..fb83696 --- /dev/null +++ b/dev/auth.env.example @@ -0,0 +1,31 @@ +HTTP_PORT=8081 +HTTP_IP_HEADER= +ENVIRONMENT=development +LOG_LEVEL=info + +DB_HOST=postgres +DB_PORT=5432 +DB_USER=unibo_user +DB_PASS=unibo_pass +DB_NAME=unibo_toolkit +DB_MAX_CONNS=10 +DB_MIN_CONNS=1 + +JWT_ISSUER=uniplanner.it +JWT_AUDIENCE=uniplanner.it +JWT_KID=key-2026-01 +ACCESS_TOKEN_TTL=900 +REFRESH_TOKEN_TTL=2592000 + +# Generated by `make dev-setup` (dev/setup.sh) — do NOT commit real keys. +JWT_PRIVATE_KEY= +JWT_PUBLIC_KEY= + +# Fill these from your LOCAL Google OAuth dev app (see dev/README.md). +# Google redirects the browser straight to the FRONTEND callback page, which then +# POSTs the code to /api/v1/auth/exchange and sets the cookies. +# Google console → Authorized redirect URI: http://localhost:3000/api/auth/callback +# → Authorized JavaScript origin: http://localhost:3000 +GOOGLE_CLIENT_ID= +GOOGLE_CLIENT_SECRET= +GOOGLE_REDIRECT_URL=http://localhost:3000/api/auth/callback diff --git a/dev/calendar.env b/dev/calendar.env new file mode 100644 index 0000000..c754df9 --- /dev/null +++ b/dev/calendar.env @@ -0,0 +1,25 @@ +HTTP_PORT=8082 +ENVIRONMENT=dev +LOG_LEVEL=info + +DB_HOST=postgres +DB_PORT=5432 +DB_USER=unibo_user +DB_PASS=unibo_pass +DB_NAME=unibo_toolkit +DB_MAX_CONNS=10 +DB_MIN_CONNS=2 + +SCRAPER_URL=http://scraper-service:8083 +SCRAPER_TIMEOUT=30 + +# Public base URL of the local gateway — generated .ics / webcal links point here +CALENDAR_BASE_URL=http://localhost:8080 +CALENDAR_PROD_ID=-//UniPlanner//Calendar//EN +CALENDAR_TIMEZONE=Europe/Rome +CALENDAR_DOMAIN=localhost + +TTL_ANONYMOUS_DAYS=30 +TTL_AUTHENTICATED_DAYS=365 + +SNOWFLAKE_NODE_ID=1 diff --git a/dev/frontend.env.example b/dev/frontend.env.example new file mode 100644 index 0000000..f49c753 --- /dev/null +++ b/dev/frontend.env.example @@ -0,0 +1,4 @@ +NEXT_PUBLIC_API_URL=http://localhost:8080 +AUTH_SERVICE_URL=http://localhost:8080 +COOKIE_SECURE=false +COOKIE_DOMAIN= diff --git a/dev/scraper.env b/dev/scraper.env new file mode 100644 index 0000000..f474505 --- /dev/null +++ b/dev/scraper.env @@ -0,0 +1,24 @@ +DEBUG_MODE=true +PORT=8083 +LOG_LEVEL=INFO + +DB_HOST=postgres +DB_PORT=5432 +DB_NAME=unibo_toolkit +DB_USER=unibo_user +DB_PASSWORD=unibo_pass + +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_DB=3 + +SKIP_STARTUP_JOBS=false +FORCE_UPDATE=false + +SCRAPER_REQUEST_TIMEOUT=30 +SCHEDULER_TIMEZONE=UTC +UPDATE_COURSES_INTERVAL_SECONDS=43200 +UPDATE_TIMETABLES_INTERVAL_SECONDS=3600 +DELAY_BETWEEN_CURRICULA_REQUESTS=0.1 +DELAY_BETWEEN_SITE_URL_REQUESTS=0.1 +DELAY_BETWEEN_TIMETABLE_REQUESTS=0.1 diff --git a/dev/setup.sh b/dev/setup.sh new file mode 100755 index 0000000..6ed964a --- /dev/null +++ b/dev/setup.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash +# One-time local dev setup: generate a dev JWT keypair and scaffold env files. +# Safe to re-run — it never overwrites Google creds you've already filled in. +set -euo pipefail + +cd "$(dirname "$0")/.." + +echo "▸ Generating dev JWT RSA keypair (dev/keys/) …" +mkdir -p dev/keys +if [ ! -f dev/keys/jwt.key ]; then + openssl genrsa -out dev/keys/jwt.key 2048 2>/dev/null + openssl rsa -in dev/keys/jwt.key -pubout -out dev/keys/jwt.pub 2>/dev/null + echo " created dev/keys/jwt.key + jwt.pub" +else + echo " reusing existing dev/keys/jwt.key" +fi + +# Join PEM lines into a single \n-escaped string (what auth-service expects). +esc() { awk 'NF{ printf "%s\\n", $0 }' "$1"; } +PRIV="$(esc dev/keys/jwt.key)" +PUB="$(esc dev/keys/jwt.pub)" + +echo "▸ Scaffolding dev/auth.env …" +[ -f dev/auth.env ] || cp dev/auth.env.example dev/auth.env +# Inject the keys (ENVIRON avoids awk backslash-escape processing), keep everything else. +PRIV="$PRIV" PUB="$PUB" awk ' + /^JWT_PRIVATE_KEY=/ { print "JWT_PRIVATE_KEY=\"" ENVIRON["PRIV"] "\""; next } + /^JWT_PUBLIC_KEY=/ { print "JWT_PUBLIC_KEY=\"" ENVIRON["PUB"] "\""; next } + { print } +' dev/auth.env > dev/auth.env.tmp && mv dev/auth.env.tmp dev/auth.env + +echo "▸ Scaffolding .env.development.local …" +if [ -f .env.development.local ]; then + echo " .env.development.local already exists — left untouched" +else + cp dev/frontend.env.example .env.development.local + echo " created .env.development.local" +fi + +echo "" +echo "Setup done. Next steps:" +echo " 1. Create a LOCAL Google OAuth dev app (see dev/README.md) and put" +echo " GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET into dev/auth.env" +echo " 2. Run: make dev" diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..a618aed --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,109 @@ +name: uniplanner-dev + +services: + postgres: + image: postgres:16-alpine + environment: + POSTGRES_DB: unibo_toolkit + POSTGRES_USER: unibo_user + POSTGRES_PASSWORD: unibo_pass + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + healthcheck: + test: ["CMD-SHELL", "pg_isready -U unibo_user -d unibo_toolkit"] + interval: 5s + timeout: 5s + retries: 10 + + redis: + image: redis:7-alpine + ports: + - "6379:6379" + volumes: + - redis_data:/data + healthcheck: + test: ["CMD", "redis-cli", "ping"] + interval: 5s + timeout: 5s + retries: 10 + + # Migrations: prod-like pre-hook + # Fresh-clone the databases repo, then golang-migrate `up`. + # Backends do not start until `migrate` completes successfully. + db-clone: + image: alpine/git:latest + entrypoint: /bin/sh + command: + - -c + - "rm -rf /repo/databases && git clone --depth 1 https://github.com/unibo-toolkit/databases.git /repo/databases" + volumes: + - migrations_repo:/repo + + migrate: + image: migrate/migrate:latest + depends_on: + postgres: + condition: service_healthy + db-clone: + condition: service_completed_successfully + volumes: + - migrations_repo:/repo + command: + - "-path=/repo/databases/migrations" + - "-database=postgresql://unibo_user:unibo_pass@postgres:5432/unibo_toolkit?sslmode=disable" + - "up" + + # Backend services (prebuilt :dev images) + auth-service: + image: ghcr.io/unibo-toolkit/auth-service:dev + pull_policy: always + ports: + - "8081:8081" + depends_on: + migrate: + condition: service_completed_successfully + env_file: + - dev/auth.env + + scraper-service: + image: ghcr.io/unibo-toolkit/scraper-service:dev + pull_policy: always + ports: + - "8083:8083" + depends_on: + migrate: + condition: service_completed_successfully + redis: + condition: service_healthy + env_file: + - dev/scraper.env + + calendar-manager: + image: ghcr.io/unibo-toolkit/calendar-manager-service:dev + pull_policy: always + ports: + - "8082:8082" + depends_on: + migrate: + condition: service_completed_successfully + env_file: + - dev/calendar.env + + # Gateway: path routing + gateway: + image: caddy:2-alpine + ports: + - "8080:8080" + depends_on: + - auth-service + - scraper-service + - calendar-manager + volumes: + - ./dev/Caddyfile:/etc/caddy/Caddyfile:ro + +volumes: + postgres_data: + redis_data: + migrations_repo: diff --git a/package-lock.json b/package-lock.json index 4a6941f..8a58bd1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,9 +25,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", - "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.2.tgz", + "integrity": "sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA==", "license": "MIT", "optional": true, "dependencies": { @@ -35,33 +35,33 @@ } }, "node_modules/@formatjs/fast-memoize": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.2.tgz", - "integrity": "sha512-vPnriihkfK0lzoQGaXq+qXH23VsYyansRTkTgo2aTG0k1NjLFyZimFVdfj4C9JkSE5dm7CEngcQ5TTc1yAyBfQ==", + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz", + "integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==", "license": "MIT" }, "node_modules/@formatjs/icu-messageformat-parser": { - "version": "3.5.4", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.4.tgz", - "integrity": "sha512-JVY39ROgLt+pIYngo6piyj4OVfZmXs/2FkC4wLS+ql1Eig/sGJKB7YwDO/5bkJFkfwaFAeIpgEiJc8hiYxNalw==", + "version": "3.5.15", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.15.tgz", + "integrity": "sha512-5o4grXKotAB3JqQuisLApHG43g17N+paoRTa92Jiz35Zvfemq0cVf4EDvuxyHAzmsJji7igaEowicLO/VmfJ8Q==", "license": "MIT", "dependencies": { - "@formatjs/icu-skeleton-parser": "2.1.4" + "@formatjs/icu-skeleton-parser": "2.1.11" } }, "node_modules/@formatjs/icu-skeleton-parser": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.4.tgz", - "integrity": "sha512-8bSFZbrlvGX11ywMZxtgkPBt5Q8/etyts7j7j+GWpOVK1g43zwMIH3LZxk43HAtEP7L/jtZ+OZaMiFTOiBj9CA==", + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.11.tgz", + "integrity": "sha512-j8cUmOJzVgkHuS0QiQ6ga76UIoLOFSAMWhs7aZJztH3aAdCOAE6vpC8KVvFB4cU10ON0y2/5oOVmPJ43s2lTwA==", "license": "MIT" }, "node_modules/@formatjs/intl-localematcher": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.3.tgz", - "integrity": "sha512-pHUjWb9NuhnMs8+PxQdzBtZRFJHlGhrURGAbm6Ltwl82BFajeuiIR3jblSa7ia3r62rXe/0YtVpUG3xWr5bFCA==", + "version": "0.8.13", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.13.tgz", + "integrity": "sha512-kHEAFOkeJSPNi7c5PaKaRjxcBrJwzzt81ifUu+8uve1EDW/VJl83KsxmqgqNZLzcFEhSliZGvx3+pk/RH0IOmg==", "license": "MIT", "dependencies": { - "@formatjs/fast-memoize": "3.1.2" + "@formatjs/fast-memoize": "3.1.7" } }, "node_modules/@img/colour": { @@ -531,15 +531,15 @@ } }, "node_modules/@next/env": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.15.tgz", - "integrity": "sha512-vcmyu5/MyFzN7CdqRHO3uHO44p/QPCZkuTUXroeUmhNP8bL5PHFEhik22JUazt+CDDoD6EpBYRCaS2pISL+/hg==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.20.tgz", + "integrity": "sha512-dXh51Wvddf8daEyBXryZZEe1FdVxEWx9lgaTseLZUtC1XP/W8Wri+Z+VPOElHlByk23CyqHdc2oVByX7wsTWsw==", "license": "MIT" }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.15.tgz", - "integrity": "sha512-6PvFO2Tzt10GFK2Ro9tAVEtacMqRmTarYMFKAnV2vYMdwWc73xzmDQyAV7SwEdMhzmiRoo7+m88DuiXlJlGeaw==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.20.tgz", + "integrity": "sha512-in0yXG7/pRBVjWeEl7f7ZZETpletSMFKXVS4GJgHENTPVrJFNJKPrYewa9rpZcvdjwFece5fZP0CK34G4PxowA==", "cpu": [ "arm64" ], @@ -553,9 +553,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.15.tgz", - "integrity": "sha512-G+YNV+z6FDZTp/+IdGyIMFqalBTaQSnvAA+X/hrt+eaTRFSznRMz9K7rTmzvM6tDmKegNtyzgufZW0HwVzEqaQ==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.20.tgz", + "integrity": "sha512-0hsFshdPnTzGJdDTHeHJ+XPUShOpnyp9pUFDwDhqctsA0Cd8NcIVGRPtptYhgYY9DjkKgCDRkXxmgRc+CgT5Wg==", "cpu": [ "x64" ], @@ -569,9 +569,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.15.tgz", - "integrity": "sha512-eVkrMcVIBqGfXB+QUC7jjZ94Z6uX/dNStbQFabewAnk13Uy18Igd1YZ/GtPRzdhtm7QwC0e6o7zOQecul4iC1w==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.20.tgz", + "integrity": "sha512-DMvkoBtAABOzE6pMZRW/xNm7sKqql3wzzzZJ1R/d/rp4BCxv6LykouD3tHjGY8WdQqGpZs11t+R9AtjPxvvljw==", "cpu": [ "arm64" ], @@ -585,9 +585,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.15.tgz", - "integrity": "sha512-RwSHKMQ7InLy5GfkY2/n5PcFycKA08qI1VST78n09nN36nUPqCvGSMiLXlfUmzmpQpF6XeBYP2KRWHi0UW3uNg==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.20.tgz", + "integrity": "sha512-RQmDfeYBtXV2FSId7dfA1hE6M/T6+g7wdbYnFQ47tw/gUBwV+CccLVejNmCGa9yLDitk83foeg8hl/3DjfYQ5g==", "cpu": [ "arm64" ], @@ -601,9 +601,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.15.tgz", - "integrity": "sha512-nplqvY86LakS+eeiuWsNWvfmK8pFcOEW7ZtVRt4QH70lL+0x6LG/m1OpJ/tvrbwjmR8HH9/fH2jzW1GlL03TIg==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.20.tgz", + "integrity": "sha512-DkWLEdKajJwdGt27M3i1VEO2kelTvZrK6Pcb7JvW2BY+nofWm7FBsBNDj7g7Pr1NuQ5PLJvqEqYa20GTsBDnKQ==", "cpu": [ "x64" ], @@ -617,9 +617,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.15.tgz", - "integrity": "sha512-eAgl9NKQ84/sww0v81DQINl/vL2IBxD7sMybd0cWRw6wqgouVI53brVRBrggqBRP/NWeIAE1dm5cbKYoiMlqDQ==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.20.tgz", + "integrity": "sha512-rAO5b7pKHvX+ExdmJskusDXTNbiNZfptifIPZItbUx+AOXxxTydVBsPt7Oz84DRd5mY8e0DcE8kvLj3AIfjE6w==", "cpu": [ "x64" ], @@ -633,9 +633,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.15.tgz", - "integrity": "sha512-GJVZC86lzSquh0MtvZT+L7G8+jMnJcldloOjA8Kf3wXvBrvb6OGe2MzPuALxFshSm/IpwUtD2mIoof39ymf52A==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.20.tgz", + "integrity": "sha512-Hp3zFsN8N8Kj9+vY6L4vnZ9EtA9eXyATu0q4EfGbZTiocgPUNSfz8NWhym6xvaOmHpJ8EuoypuU1WejCPsTFtg==", "cpu": [ "arm64" ], @@ -649,9 +649,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.15.tgz", - "integrity": "sha512-nFucjVdwlFqxh/JG3hWSJ4p8+YJV7Ii8aPDuBQULB6DzUF4UNZETXLfEUk+oI2zEznWWULPt7MeuTE6xtK1HSA==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.20.tgz", + "integrity": "sha512-T/L7CXpR1M0wij/xbF3rT1+7KvSkfOLr7C+ToHHWZTG2eKmb52C5WvsyGCBNtkVvDEUESWkRUbbqSH4rSbOCYQ==", "cpu": [ "x64" ], @@ -966,9 +966,9 @@ "license": "MIT" }, "node_modules/@swc/core-darwin-arm64": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.26.tgz", - "integrity": "sha512-OmcP96CFsNOwa65tamQayRcfqhNlcQ3YCWOq+0Wb+CAM4uB7kOMrXY41Gj4atthxrGhLQ9pg7Vk26iApb88idA==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.15.46.tgz", + "integrity": "sha512-IsISIT22EfktVJrlvIpnAxG2u/A9aob9l99HMlx80x72WlFmFPk1V3UhkEzx86eJP8hw049KTFv/RISho2cq2Q==", "cpu": [ "arm64" ], @@ -982,9 +982,9 @@ } }, "node_modules/@swc/core-darwin-x64": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.26.tgz", - "integrity": "sha512-liTTTpKSv89ivIxcZ+iU1cRige9Y7JkOjVnJ2Ystzl+DsWNHqt7wLTTgm/u7gEqmmAS2JKryODLQn3q1UtFNPQ==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.15.46.tgz", + "integrity": "sha512-4Tj4ppVIPCmUMpmGFiGtyEriwLyJ+yi/US4WfBrP/ok8COGddDZXLEzQETnKyK46mjvr1v0jevrS23zjoff7vA==", "cpu": [ "x64" ], @@ -998,9 +998,9 @@ } }, "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.26.tgz", - "integrity": "sha512-Y/g+m3I8CeBof5A3kWWOS6QA2HOIUytF5EeTgfwcAK+GKT/tGe7Xqo5svBtaqflU5od2zzbMTWqkinPXgRWGgA==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.15.46.tgz", + "integrity": "sha512-i8tUGnNjyOgMmfmgFSg4aeJLQoFyfpIHK5FjpQAwpRyQIqEUB2w1e8zIDQzY1WhOxx8NoS1S5iUL813Un4Sf5A==", "cpu": [ "arm" ], @@ -1014,9 +1014,9 @@ } }, "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.26.tgz", - "integrity": "sha512-19IvwyPfBN/rz9s7qXhOTQmW0922+pjpRUUvIebu+CMM75nX6YuDzHsGx8hSmn5dS89SNaMCh1lgUuXqm++6jg==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.15.46.tgz", + "integrity": "sha512-c0OnhqzdhfOvv6qhNCcByepB+sNYOGZyhtr2Qa6ZCHvAWTYhSRw4j/u92Stue9PbZ/6q74b9nHzi76+kVzqQHQ==", "cpu": [ "arm64" ], @@ -1030,9 +1030,9 @@ } }, "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.26.tgz", - "integrity": "sha512-iNlbvTIo425rkKzDLLWFJGnFXr3myETUdIDHcjuiPNZE8b0ogmcAuilC4yEJX7FSHGbnlsoJcCT2xf4b3VJmmQ==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.15.46.tgz", + "integrity": "sha512-imyRpNEcUzFQFV2LE4jL68ErvmKEuZCbvZru77iQREunJ+bR4i658cupTgtG1mLYM3F1Tzy3Sb9xYb02KghWTg==", "cpu": [ "arm64" ], @@ -1046,9 +1046,9 @@ } }, "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.26.tgz", - "integrity": "sha512-AuuEOtG+YXKIjIUup4RsxYNklx6XVB3WKWfhxG6hnfPrn7vp89RNOLbbyyprgj6Sk7k9ulwGVTJElEvmBNPSCA==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.15.46.tgz", + "integrity": "sha512-ctEfcl/HcUeomK33cbySiHZm98GEDIxTm1EkpBsYCiHxElYBzvTXVeuQT2YwbUXn9XCrjiw4ipyUNk33k26qRg==", "cpu": [ "ppc64" ], @@ -1062,9 +1062,9 @@ } }, "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.26.tgz", - "integrity": "sha512-JcMDWQvW1BchUyRg8E0jHiTx7CQYpUr5uDEL1dnPDECrEjBEGG2ynmJ3XX70sWXql0JagqR1t3VpANYFWdUnqA==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.15.46.tgz", + "integrity": "sha512-DxlMdnt84TtRVTv7WL/thWyz9+QU8QZNNoAP9rrk0P68LziuhfePp8MjQ44zIprpTHTsEwyziIuGUUN5iSC1bQ==", "cpu": [ "s390x" ], @@ -1078,9 +1078,9 @@ } }, "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.26.tgz", - "integrity": "sha512-FW7V7Mbpq4+PA7BiAq76LJs8MdNuUSylyuRVfQRkhIyeWadFroZ+KOPgjku8Z/fXzngxBRvsk+PGGB0t8mGcjA==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.15.46.tgz", + "integrity": "sha512-SKxI7J6t90XPl8hRUqtJi9NfGdunN/E/vZMc7Bc0figeRdOPDBT+Tm8g7cx9xM0T0mewh2l+8dewa3Am27/P+A==", "cpu": [ "x64" ], @@ -1094,9 +1094,9 @@ } }, "node_modules/@swc/core-linux-x64-musl": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.26.tgz", - "integrity": "sha512-w8erqMHsVcdGwUfJxF6LaiTuPoKnyLOcUbhLcxiXrlLt5MLjtlgcIeUY/NWK/oPoyqkgH+/i8pOJnMTxvl83ZQ==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.15.46.tgz", + "integrity": "sha512-qj9T6B7bosI0VEsrWOVXZN1OXxS8Tp63ywyrLxNdOycnUtLdkgYcoBsN5y8ImnDDsnwrEWZOy1e+J4xSe7mA3Q==", "cpu": [ "x64" ], @@ -1110,9 +1110,9 @@ } }, "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.26.tgz", - "integrity": "sha512-uDCWCNpUiqkbvPmsuPUTn/P7ag9SqNXD2JT/W3dUu7yZ2krzN+nmmoQ2xRX63/J6RYiHI7aT4jo7Z++lsljlPA==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.15.46.tgz", + "integrity": "sha512-8p7l4c3LU+eA5g9Et1JPhNeMC1oQwXTGU+uah8DPIBX7YXzqswvaBtyKVmXefVGi/DJU1x3YJsc3mbAp9aWzSQ==", "cpu": [ "arm64" ], @@ -1126,9 +1126,9 @@ } }, "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.26.tgz", - "integrity": "sha512-2k1ax1QmmqLEnpC0uRCw7OXhBfyvdPqERBXupDasjYbChT6ZSO/uha28Bp38cw0viKIG79L27aTDkbkABsMW3w==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.15.46.tgz", + "integrity": "sha512-tUEnfr3Bn9u6FOjUb3PN9p+09qZC2j+wNDLKHzXXZn22rqGcUqR/ohCRSS+nG9B9+X+U+3FewNEHJkTmdIvMjQ==", "cpu": [ "ia32" ], @@ -1142,9 +1142,9 @@ } }, "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.26.tgz", - "integrity": "sha512-aUuYecSEGa4SUSdyCWaI/vk8jdseifYnsF1GZQx2+piL8GIuT/5QrVcFfmes4Iwy7FIVXxtzD063z/FfpZ7K7w==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.15.46.tgz", + "integrity": "sha512-Vux7UDzBJYQggSuPfcl2w9iu+IJpgpRCxHzgCaVkELnAXAE4XZMOTX9HNcaNiwfeIDqdu2rkr69RuDm6wY8neA==", "cpu": [ "x64" ], @@ -1173,18 +1173,18 @@ } }, "node_modules/@swc/types": { - "version": "0.1.26", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.26.tgz", - "integrity": "sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==", + "version": "0.1.27", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.27.tgz", + "integrity": "sha512-K6h3iUlqeM946U4sXFYeahefR1YBbXJvko+hv8WS8/0BNJ4OHiHRywMnQUJCqkR7Y9+hqQ1TvEpiKqUhz7NEFg==", "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" } }, "node_modules/@tanstack/query-core": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.99.0.tgz", - "integrity": "sha512-3Jv3WQG0BCcH7G+7lf/bP8QyBfJOXeY+T08Rin3GZ1bshvwlbPt7NrDHMEzGdKIOmOzvIQmxjk28YEQX60k7pQ==", + "version": "5.101.2", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.101.2.tgz", + "integrity": "sha512-hH5MLoJhF7KaIGd7q3xTXGXvslI+GYlM1Z/35aSHHWaCJWB7XvTSHYuV3eM7tw+aE0mT/xMro4M4Q9rCGHT0lw==", "license": "MIT", "funding": { "type": "github", @@ -1192,12 +1192,12 @@ } }, "node_modules/@tanstack/react-query": { - "version": "5.99.0", - "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.99.0.tgz", - "integrity": "sha512-OY2bCqPemT1LlqJ8Y2CUau4KELnIhhG9Ol3ZndPbdnB095pRbPo1cHuXTndg8iIwtoHTgwZjyaDnQ0xD0mYwAw==", + "version": "5.101.2", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.101.2.tgz", + "integrity": "sha512-seDkr6kzGzX1okaaTtZPtgA688CDPlXUz1C6xSg0ESqn04Vuc8tlrYms1s3de+znBqhPVxFRfpAfUf+6XvfPWg==", "license": "MIT", "dependencies": { - "@tanstack/query-core": "5.99.0" + "@tanstack/query-core": "5.101.2" }, "funding": { "type": "github", @@ -1208,9 +1208,9 @@ } }, "node_modules/@types/node": { - "version": "22.19.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.17.tgz", - "integrity": "sha512-wGdMcf+vPYM6jikpS/qhg6WiqSV/OhG+jeeHT/KlVqxYfD40iYJf9/AE1uQxVWFvU7MipKRkRv8NSHiCGgPr8Q==", + "version": "22.20.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.1.tgz", + "integrity": "sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==", "dev": true, "license": "MIT", "dependencies": { @@ -1218,9 +1218,9 @@ } }, "node_modules/@types/react": { - "version": "19.2.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", - "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "version": "19.2.17", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.17.tgz", + "integrity": "sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1237,6 +1237,18 @@ "@types/react": "^19.2.0" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1244,13 +1256,14 @@ "license": "MIT" }, "node_modules/axios": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.15.0.tgz", - "integrity": "sha512-wWyJDlAatxk30ZJer+GeCWS209sA42X+N5jU2jy6oHTp7ufw8uzUTVFBX9+wTfAlhiJXGS0Bq7X6efruWjuK9Q==", + "version": "1.18.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.1.tgz", + "integrity": "sha512-3nTvFlvpn9Zu/RkHUqtc7/+al4UpRW5az71ap5zccp6e8RAYEzhMTecX8Dz1wWDYrPpUoB1HAQEGEAEvUr7S9g==", "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.11", + "follow-redirects": "^1.16.0", "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, @@ -1268,9 +1281,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001788", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001788.tgz", - "integrity": "sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==", + "version": "1.0.30001806", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001806.tgz", + "integrity": "sha512-72Cuvd95zbSYPKq6Fhg8eDJRlzgWDf7/mtoZv6Qe/DYNCEBdNxoA3+rZAU2ZhGCpZlns3EssFavaZomckT5Uuw==", "funding": [ { "type": "opencollective", @@ -1312,6 +1325,23 @@ "devOptional": true, "license": "MIT" }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -1363,9 +1393,9 @@ } }, "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0" @@ -1410,29 +1440,29 @@ } }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" } }, "node_modules/framer-motion": { - "version": "12.38.0", - "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.38.0.tgz", - "integrity": "sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==", + "version": "12.42.2", + "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.42.2.tgz", + "integrity": "sha512-5XY9luDiu0oHfHBjpDthFMh0ES+122w6p/papSJBweMkO8Sn+PW2QaEgRblQBpWFnuvZS5qvarpt/hO2pjGmnw==", "license": "MIT", "dependencies": { - "motion-dom": "^12.38.0", - "motion-utils": "^12.36.0", + "motion-dom": "^12.42.2", + "motion-utils": "^12.39.0", "tslib": "^2.4.0" }, "peerDependencies": { @@ -1538,9 +1568,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -1549,10 +1579,23 @@ "node": ">= 0.4" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/icu-minify": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.9.1.tgz", - "integrity": "sha512-6NkfF9GHHFouqnz+wuiLjCWQiyxoEyJ5liUv4Jxxo/8wyhV7MY0L0iTEGDAVEa4aAD58WqTxFMa20S5nyMjwNw==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.13.2.tgz", + "integrity": "sha512-XhYQTEnBXBCyF6ERiwItFweoOXDUciujaGjIWCA7RhOCEPDfhVSTtuwfRq6HdVk7tKnYJh+yaurP96zGnaKsPg==", "funding": [ { "type": "individual", @@ -1565,13 +1608,13 @@ } }, "node_modules/intl-messageformat": { - "version": "11.2.1", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.1.tgz", - "integrity": "sha512-1gAVEUt3wEPvTqML4Fsw9klZV5j0vszQxayP/fi6gUroAc8AUHiNaisBKLWxybL1AdWq1mP07YV1q8v4N92ilQ==", + "version": "11.2.12", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.12.tgz", + "integrity": "sha512-KW70Xxfcvy7vV3qODfvShWkFDPMqKDAa4N+hSyVBWGNtVhTUFYaqlD/l88DaYPKiVcPP4rPQ3qnH7i5K82Mg7g==", "license": "BSD-3-Clause", "dependencies": { - "@formatjs/fast-memoize": "3.1.2", - "@formatjs/icu-messageformat-parser": "3.5.4" + "@formatjs/fast-memoize": "3.1.7", + "@formatjs/icu-messageformat-parser": "3.5.15" } }, "node_modules/is-extglob": { @@ -1626,24 +1669,30 @@ } }, "node_modules/motion-dom": { - "version": "12.38.0", - "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.38.0.tgz", - "integrity": "sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==", + "version": "12.42.2", + "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.42.2.tgz", + "integrity": "sha512-5gIMWLp/PycBtJRJWRgjxke5n8dlvkSn2DrYW+tr3XcqAZY1xZh6BJyooJXCM8wdfM7wfMjkBJNLge1CKPUIRA==", "license": "MIT", "dependencies": { - "motion-utils": "^12.36.0" + "motion-utils": "^12.39.0" } }, "node_modules/motion-utils": { - "version": "12.36.0", - "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.36.0.tgz", - "integrity": "sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==", + "version": "12.39.0", + "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.39.0.tgz", + "integrity": "sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "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.16", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.16.tgz", + "integrity": "sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==", "funding": [ { "type": "github", @@ -1668,12 +1717,12 @@ } }, "node_modules/next": { - "version": "15.5.15", - "resolved": "https://registry.npmjs.org/next/-/next-15.5.15.tgz", - "integrity": "sha512-VSqCrJwtLVGwAVE0Sb/yikrQfkwkZW9p+lL/J4+xe+G3ZA+QnWPqgcfH1tDUEuk9y+pthzzVFp4L/U8JerMfMQ==", + "version": "15.5.20", + "resolved": "https://registry.npmjs.org/next/-/next-15.5.20.tgz", + "integrity": "sha512-cvyS3/geydan1xLtE3FA8VCgdoQ/Gg/dlOldFkFCbB5VcVYJV7090hQLBnvTW2PwT76Z/dHdzDZCsVhZpoOlUA==", "license": "MIT", "dependencies": { - "@next/env": "15.5.15", + "@next/env": "15.5.20", "@swc/helpers": "0.5.15", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", @@ -1686,14 +1735,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.5.15", - "@next/swc-darwin-x64": "15.5.15", - "@next/swc-linux-arm64-gnu": "15.5.15", - "@next/swc-linux-arm64-musl": "15.5.15", - "@next/swc-linux-x64-gnu": "15.5.15", - "@next/swc-linux-x64-musl": "15.5.15", - "@next/swc-win32-arm64-msvc": "15.5.15", - "@next/swc-win32-x64-msvc": "15.5.15", + "@next/swc-darwin-arm64": "15.5.20", + "@next/swc-darwin-x64": "15.5.20", + "@next/swc-linux-arm64-gnu": "15.5.20", + "@next/swc-linux-arm64-musl": "15.5.20", + "@next/swc-linux-x64-gnu": "15.5.20", + "@next/swc-linux-x64-musl": "15.5.20", + "@next/swc-win32-arm64-msvc": "15.5.20", + "@next/swc-win32-x64-msvc": "15.5.20", "sharp": "^0.34.3" }, "peerDependencies": { @@ -1720,9 +1769,9 @@ } }, "node_modules/next-intl": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.9.1.tgz", - "integrity": "sha512-N7ga0CjtYcdxNvaKNIi6eJ2mmatlHK5hp8rt0YO2Omoc1m0gean242/Ukdj6+gJNiReBVcYIjK0HZeNx7CV1ug==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.13.2.tgz", + "integrity": "sha512-iCYycEP7/PE+1ue4MWBuz1qns6ESA+SGzuXxNMNN1qiYUh2fLhJPiZvHW1zZC7zMTn44aJUglOVZxUb1+hUz6g==", "funding": [ { "type": "individual", @@ -1734,11 +1783,11 @@ "@formatjs/intl-localematcher": "^0.8.1", "@parcel/watcher": "^2.4.1", "@swc/core": "^1.15.2", - "icu-minify": "^4.9.1", + "icu-minify": "^4.13.2", "negotiator": "^1.0.0", - "next-intl-swc-plugin-extractor": "^4.9.1", + "next-intl-swc-plugin-extractor": "^4.13.2", "po-parser": "^2.1.1", - "use-intl": "^4.9.1" + "use-intl": "^4.13.2" }, "peerDependencies": { "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", @@ -1751,20 +1800,20 @@ } }, "node_modules/next-intl-swc-plugin-extractor": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.9.1.tgz", - "integrity": "sha512-8whJJ6oxJz8JqkHarggmmuEDyXgC7nEnaPhZD91CJwEWW4xp0AST3Mw17YxvHyP2vAF3taWfFbs1maD+WWtz3w==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.13.2.tgz", + "integrity": "sha512-O30N/Y4ifzRe5Sz80jD1Qkg4VY6Zfef4SbHNNE166QkHswBf3/Kygpdd1X52sUUwTCk6uvdisO8ybfAN/VYJHQ==", "license": "MIT" }, "node_modules/next-intl/node_modules/@swc/core": { - "version": "1.15.26", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.26.tgz", - "integrity": "sha512-tglZGyx8N5PC+x1Nd/JrZxqpqlcZoSuG9gTDKO6AuFToFiVB3uS8HvbKFuO7g3lJzvFf9riAb94xs9HU2UhAHQ==", + "version": "1.15.46", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.15.46.tgz", + "integrity": "sha512-Ri3em2mBpq3h2zSPliCYl63otDGqek8PPEfv2nWgRQEbZ/VBCNyypVTVQ6cEbTCXBhy+WE2T3fQb08moIyuYaw==", "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.26" + "@swc/types": "^0.1.27" }, "engines": { "node": ">=10" @@ -1774,18 +1823,18 @@ "url": "https://opencollective.com/swc" }, "optionalDependencies": { - "@swc/core-darwin-arm64": "1.15.26", - "@swc/core-darwin-x64": "1.15.26", - "@swc/core-linux-arm-gnueabihf": "1.15.26", - "@swc/core-linux-arm64-gnu": "1.15.26", - "@swc/core-linux-arm64-musl": "1.15.26", - "@swc/core-linux-ppc64-gnu": "1.15.26", - "@swc/core-linux-s390x-gnu": "1.15.26", - "@swc/core-linux-x64-gnu": "1.15.26", - "@swc/core-linux-x64-musl": "1.15.26", - "@swc/core-win32-arm64-msvc": "1.15.26", - "@swc/core-win32-ia32-msvc": "1.15.26", - "@swc/core-win32-x64-msvc": "1.15.26" + "@swc/core-darwin-arm64": "1.15.46", + "@swc/core-darwin-x64": "1.15.46", + "@swc/core-linux-arm-gnueabihf": "1.15.46", + "@swc/core-linux-arm64-gnu": "1.15.46", + "@swc/core-linux-arm64-musl": "1.15.46", + "@swc/core-linux-ppc64-gnu": "1.15.46", + "@swc/core-linux-s390x-gnu": "1.15.46", + "@swc/core-linux-x64-gnu": "1.15.46", + "@swc/core-linux-x64-musl": "1.15.46", + "@swc/core-win32-arm64-msvc": "1.15.46", + "@swc/core-win32-ia32-msvc": "1.15.46", + "@swc/core-win32-x64-msvc": "1.15.46" }, "peerDependencies": { "@swc/helpers": ">=0.5.17" @@ -1797,11 +1846,12 @@ } }, "node_modules/next-intl/node_modules/@swc/helpers": { - "version": "0.5.21", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.21.tgz", - "integrity": "sha512-jI/VAmtdjB/RnI8GTnokyX7Ug8c+g+ffD6QRLa6XQewtnGyukKkKSk3wLTM3b5cjt1jNh9x0jfVlagdN2gDKQg==", - "extraneous": true, + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", "license": "Apache-2.0", + "optional": true, + "peer": true, "dependencies": { "tslib": "^2.8.0" } @@ -1819,9 +1869,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "license": "MIT", "engines": { "node": ">=12" @@ -1874,24 +1924,24 @@ } }, "node_modules/react": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react/-/react-19.2.5.tgz", - "integrity": "sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.7.tgz", + "integrity": "sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/react-dom": { - "version": "19.2.5", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.5.tgz", - "integrity": "sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==", + "version": "19.2.7", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.7.tgz", + "integrity": "sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==", "license": "MIT", "dependencies": { "scheduler": "^0.27.0" }, "peerDependencies": { - "react": "^19.2.5" + "react": "^19.2.7" } }, "node_modules/scheduler": { @@ -1901,9 +1951,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "optional": true, "bin": { @@ -2018,9 +2068,9 @@ "license": "MIT" }, "node_modules/use-intl": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.9.1.tgz", - "integrity": "sha512-iGVV/xFYlhe3btafRlL8RPLD2Jsuet4yqn9DR6LWWbMhULsJnXgLonDkzDmsAIBIwFtk02oJuX/Ox2vwHKF+UQ==", + "version": "4.13.2", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.13.2.tgz", + "integrity": "sha512-p6/gCromeBoec+wEuOIkPaytH77RBjW94KWv8MRsNrbI4UOx8QgwNhgl9lbPOKM/b0dpanLhCYztLEH5yQUjtg==", "funding": [ { "type": "individual", @@ -2031,7 +2081,7 @@ "dependencies": { "@formatjs/fast-memoize": "^3.1.0", "@schummar/icu-type-parser": "1.21.5", - "icu-minify": "^4.9.1", + "icu-minify": "^4.13.2", "intl-messageformat": "^11.1.0" }, "peerDependencies": { @@ -2039,9 +2089,9 @@ } }, "node_modules/zustand": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", - "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "version": "5.0.14", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.14.tgz", + "integrity": "sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==", "license": "MIT", "engines": { "node": ">=12.20.0" diff --git a/src/app/[locale]/LandingPageClient.tsx b/src/app/[locale]/LandingPageClient.tsx index c0c693a..9be6dc5 100644 --- a/src/app/[locale]/LandingPageClient.tsx +++ b/src/app/[locale]/LandingPageClient.tsx @@ -7,6 +7,7 @@ import type { Locale } from '@/i18n/routing' import Nav from '@/components/layout/Nav' import Footer from '@/components/layout/Footer' import Button from '@/components/ui/Button' +import { ArrowRight, ArrowDown } from '@/components/ui/icons' import { usePublicStats } from '@/hooks/usePublicStats' import styles from './page.module.css' @@ -422,18 +423,3 @@ function FeatureItem({ icon, title, desc, note }: { icon: string; title: string; ) } -function ArrowRight() { - return ( - - ) -} - -function ArrowDown() { - return ( - - ) -} diff --git a/src/app/[locale]/auth/page.module.css b/src/app/[locale]/auth/page.module.css index ad430e5..54f8a0c 100644 --- a/src/app/[locale]/auth/page.module.css +++ b/src/app/[locale]/auth/page.module.css @@ -25,26 +25,6 @@ color: var(--fg-100); } -.authButton { - display: flex; - align-items: center; - justify-content: center; - gap: var(--spacing-12); - width: 100%; - padding: var(--spacing-14) var(--spacing-22); - border: 1px solid var(--border); - border-radius: var(--radius-sm); - font-size: var(--text-base); - color: var(--fg-150); - text-decoration: none; - transition: background-color 0.15s; - height: 48px; -} - -.authButton:hover { - background-color: var(--bg-150); -} - .agree { font-size: var(--text-lg); color: var(--fg-200); diff --git a/src/app/[locale]/auth/page.tsx b/src/app/[locale]/auth/page.tsx index 8a88fa8..eee4174 100644 --- a/src/app/[locale]/auth/page.tsx +++ b/src/app/[locale]/auth/page.tsx @@ -4,6 +4,7 @@ import { useSearchParams } from 'next/navigation' import { Suspense } from 'react' import { useTranslations } from 'next-intl' import { Link } from '@/i18n/navigation' +import Button from '@/components/ui/Button' import styles from './page.module.css' function AuthContent() { @@ -29,16 +30,36 @@ function AuthContent() {
{t('agree')}
{t('agreeOur')}{' '}
diff --git a/src/app/[locale]/dashboard/calendars/page.tsx b/src/app/[locale]/dashboard/calendars/page.tsx
index aa3654c..00635c8 100644
--- a/src/app/[locale]/dashboard/calendars/page.tsx
+++ b/src/app/[locale]/dashboard/calendars/page.tsx
@@ -9,6 +9,7 @@ import { useCalendars } from '@/hooks/useCalendars'
import CalendarCard from '@/components/dashboard/CalendarCard'
import ShareModal from '@/components/dashboard/ShareModal'
import Button from '@/components/ui/Button'
+import { Plus, ArrowRight } from '@/components/ui/icons'
import Input from '@/components/ui/Input'
import Skeleton from '@/components/ui/Skeleton'
import type { CalendarListItem } from '@/types/calendar'
@@ -77,7 +78,7 @@ export default function CalendarsPage() {
{t('emptyDesc')}
-{et('accessDeniedDesc')}
-{et('calendarNotFoundDesc')}
-{et('serverErrorDesc')}
-{dt('deleteCalendarConfirm')}
+{t('noCalendars')}
+{t('emptyDesc')}
+ +{t('logoutAllDesc')}
{t('deleteAccountDesc')}
-{t('deleteConfirm')}
{t('serverErrorDesc')}
-