diff --git a/.github/workflows/lint-checker.yml b/.github/workflows/lint-checker.yml index 0d83993..9ea83d6 100644 --- a/.github/workflows/lint-checker.yml +++ b/.github/workflows/lint-checker.yml @@ -1,9 +1,6 @@ name: Frontend Lint Checker on: - push: - paths: - - 'frontend/**' pull_request: paths: - 'frontend/**' diff --git a/.gitignore b/.gitignore index ffdcf9d..e3ae6fc 100644 --- a/.gitignore +++ b/.gitignore @@ -59,5 +59,10 @@ desktop.ini *.bak *.backup +# Lock file for pnpm +pnpm-lock.yaml /repo-to-text/ + +# Typescript build info +frontend/tsconfig.tsbuildinfo diff --git a/README.md b/README.md index c5fc1f0..89d5379 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,28 @@ -# MPloy Job Board +# MAC Job Board -A modern job board application built with Next.js, Spring Boot, and Go, designed to help students find internships and job opportunities. +Hey there! 👋 -### Frontend -- Next.js 15 with App Router -- React 19 -- TypeScript -- Mantine UI -- Tailwind CSS +This is a modern, intelligent job board platform that automatically aggregates job listings, providing users with a streamlined experience to search, filter, and discover relevant opportunities. The platform updates daily with fresh listings through our smart AI robots. -### Backend -- Spring Boot (Kotlin) -- Redis for caching -- RESTful APIs +## Features 🚀 +- Jobs update automatically every single day from various sources (automatically deduplicated) +- We use AI to help fix, sort and summarise the listings +- You can filter for exactly what you want (e.g. Big Tech Intern Roles for Interational students) +- Works perfectly on phone or laptop + +## Frontend + +- Next.js 15: Utilizing the App Router for server-side rendering and optimized client-side navigation +- React 19: For building the interactive user interface components +- TypeScript: Ensuring type safety across the codebase +- Mantine UI: For consistent, accessible UI components +- Tailwind CSS: For utility-first styling and responsive design + +## Backend + +- Server Actions: Answers search and feedback requests +- MongoDB: Stores job listings and related metadata +- GoLang: Powers our web robots (this part is not open source) ## Getting Started @@ -53,7 +63,6 @@ cd backend Examples: - backend/edwn/redis-caching - frontend/sarah/job-filters - - fullstack/alex/docker-setup ``` ## License diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev index a6756ee..5ad26ad 100644 --- a/backend/Dockerfile.dev +++ b/backend/Dockerfile.dev @@ -1,5 +1,7 @@ FROM gradle:8.12-jdk21 AS build WORKDIR /app +ENV HOME /root COPY . . RUN chmod +x ./gradlew -CMD ["./gradlew", "bootRun"] \ No newline at end of file +RUN chmod +x start.sh && ./gradlew getDependencies +CMD ["sh", "start.sh"] diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts index e266b53..b17dc34 100644 --- a/backend/build.gradle.kts +++ b/backend/build.gradle.kts @@ -19,6 +19,7 @@ repositories { } dependencies { + developmentOnly("org.springframework.boot:spring-boot-devtools") implementation("org.springframework.boot:spring-boot-starter") implementation("org.jetbrains.kotlin:kotlin-reflect") implementation("org.springframework.boot:spring-boot-starter-web") @@ -29,4 +30,20 @@ kotlin { compilerOptions { freeCompilerArgs.addAll("-Xjsr305=strict") } -} \ No newline at end of file +} + +tasks.register("getDependencies") { + from(sourceSets.main.get().runtimeClasspath) + into("runtime/") + + doFirst { + val runtimeDir = File("runtime") + runtimeDir.deleteRecursively() + runtimeDir.mkdir() + } + + doLast { + File("runtime").deleteRecursively() + } +} + diff --git a/backend/src/main/kotlin/com/mac/mploy/controller/HelloController.kt b/backend/src/main/kotlin/com/mac/mploy/controller/HelloController.kt index ac9f4a8..56c6fb3 100644 --- a/backend/src/main/kotlin/com/mac/mploy/controller/HelloController.kt +++ b/backend/src/main/kotlin/com/mac/mploy/controller/HelloController.kt @@ -27,4 +27,4 @@ class HelloController( throw e } } -} \ No newline at end of file +} diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml index b4610fb..6df13db 100644 --- a/backend/src/main/resources/application.yml +++ b/backend/src/main/resources/application.yml @@ -17,4 +17,13 @@ logging: io.lettuce.core: DEBUG server: - port: 8080 \ No newline at end of file + port: 8080 + +devtools: + restart: + enabled: true + +# Enable LiveReload if needed +spring.devtools.livereload.enabled: true + +spring.devtools.reload.trigger-file: .reloadtrigger diff --git a/backend/start.sh b/backend/start.sh new file mode 100644 index 0000000..c0998a4 --- /dev/null +++ b/backend/start.sh @@ -0,0 +1,6 @@ +# Start spring boot devtools process in the background. +# This is necessary for hot reload. +(./gradlew -t :bootJar) & +# Next, start the app. +# The "PskipDownload" option ensures dependencies are not downloaded again. +./gradlew bootRun -PskipDownload=true --parallel --build-cache --continuous diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 4a97906..3e4d169 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -1,41 +1,48 @@ -version: '3.8' - -services: - redis: - image: redis:alpine - ports: - - "6379:6379" - networks: - - mploy-network - - backend: - build: - context: ./backend - dockerfile: Dockerfile.dev - ports: - - "8080:8080" - environment: - - REDIS_HOST=redis - - REDIS_PORT=6379 - networks: - - mploy-network - depends_on: - - redis - - frontend: - build: - context: ./frontend - dockerfile: Dockerfile.dev - ports: - - "3000:3000" - volumes: - - ./frontend:/app - - /app/node_modules - environment: - - NEXT_PUBLIC_API_URL=http://localhost:8080 - networks: - - mploy-network - -networks: - mploy-network: - driver: bridge \ No newline at end of file +services: + # redis: + # image: redis:alpine + # ports: + # - "6379:6379" + # networks: + # - mploy-network + + # backend: + # build: + # context: ./backend + # dockerfile: Dockerfile.dev + # develop: + # watch: + # - action: rebuild + # path: ./backend/src # Watch the entire src directory + # volumes: + # - ./backend:/app + # ports: + # - "35729:35729" + # - "8080:8080" + # environment: + # - SPRING_PROFILES_ACTIVE=dev + # - SPRING_DEVTOOLS_RESTART_ENABLED=true + # - REDIS_HOST=redis + # - REDIS_PORT=6379 + # networks: + # - mploy-network + # depends_on: + # - redis + + frontend: + build: + context: ./frontend + dockerfile: Dockerfile.dev + ports: + - "3000:3000" + volumes: + - ./frontend:/app + - /app/node_modules + environment: + - NEXT_PUBLIC_API_URL=http://localhost:8080 + networks: + - mploy-network + +networks: + mploy-network: + driver: bridge diff --git a/frontend/.env b/frontend/.env deleted file mode 100644 index cc53660..0000000 --- a/frontend/.env +++ /dev/null @@ -1 +0,0 @@ -NEXT_PUBLIC_API_URL=http://localhost:8080 \ No newline at end of file diff --git a/frontend/.gitignore b/frontend/.gitignore index bad0887..05540d4 100644 --- a/frontend/.gitignore +++ b/frontend/.gitignore @@ -37,4 +37,56 @@ jspm_packages .node_repl_history .next -.idea \ No newline at end of file +.idea + +# Node.js +node_modules/ +yarn-debug.log* +yarn-error.log* + +# MonoRepo Specific +/packages/*/node_modules/ +**/dist/ +**/build/ + +# OS-specific +.DS_Store +Thumbs.db + +# IDEs and Editors +.idea/ +.vscode/ +*.swp +*.swo + +# Testing +coverage/ +**/__pycache__/ +*.py[cod] +*.sqlite + +# Miscellaneous +.env +.env.local +*.local +*.lock +*.bak +*.tmp +*.backup +*.orig + +# Ignored by custom tooling +*.cache +*.output +*.pidfile + +# System Files +*.iml +*.sublime-workspace +*.sublime-project +desktop.ini + +/repo-to-text/ + +# TypeScript build info +tsconfig.tsbuildinfo \ No newline at end of file diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev index 8590b6a..2e24735 100644 --- a/frontend/Dockerfile.dev +++ b/frontend/Dockerfile.dev @@ -1,6 +1,7 @@ FROM node:22-alpine WORKDIR /app COPY package*.json ./ -RUN npm install +RUN npm ci COPY . . +RUN npm list mongodb CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md index 31de0e7..d0ceae8 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -87,7 +87,7 @@ MPloy is a very simple job board with search, filter and just a website that dis │ │ │ └── nav-bar.tsx # Navigation bar │ ├── context │ │ ├── jobs -│ │ │ └── jobs-context.tsx # Job state and actions context +│ │ │ └── filter-context.tsx # Job state and actions context │ │ │ └── jobs-provider.tsx # Provider wrapper with initial state │ ├── hooks │ │ ├── use-job-filters.ts # Filter logic and state management @@ -167,10 +167,10 @@ useEffect(() => { When user searches: 1. SearchBar component calls useJobSearch().updateSearch() -2. useJobSearch updates JobsContext filters +2. useJobSearch updates FilterContext filters 3. useUrlState syncs new state to URL 4. useJobSearch triggers API call with new filters -5. Results update in JobsContext +5. Results update in FilterContext 6. JobList component re-renders with new data When user opens job details: diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 068f470..e2eab00 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,24 +8,35 @@ "name": "frontend", "version": "0.1.0", "dependencies": { - "@mantine/core": "^7.16.1", + "@mantine/core": "^7.17.0", "@mantine/hooks": "^7.16.1", - "@tabler/icons-react": "^3.28.1", - "next": "15.1.5", + "@mantine/notifications": "^7.17.1", + "@next/third-parties": "^15.2.0", + "@tabler/icons-react": "^3.30.0", + "@tailwindcss/typography": "^0.5.16", + "@vercel/analytics": "^1.5.0", + "@vercel/speed-insights": "^1.2.0", + "dompurify": "^3.2.3", + "isomorphic-dompurify": "^2.22.0", + "jsdom": "^26.0.0", + "mongodb": "^6.14.2", + "next": "15.1.7", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/eslintrc": "^3", - "@types/node": "^20", + "@next/eslint-plugin-next": "^15.1.7", + "@types/node": "^22", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "15.1.5", - "postcss": "^8.5.1", + "eslint-config-next": "15.1.7", + "eslint-plugin-react-hooks": "^5.1.0", + "postcss": "^8.5.3", "postcss-preset-mantine": "^1.17.0", "postcss-simple-vars": "^7.0.1", - "prettier": "3.4.2", + "prettier": "3.5.3", "tailwindcss": "^3.4.1", "typescript": "^5" } @@ -34,7 +45,6 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -43,10 +53,24 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@asamuzakjp/css-color": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-2.8.3.tgz", + "integrity": "sha512-GIc76d9UI1hCvOATjZPyHFmE5qhRccp3/zGfMPapK3jBi+yocEzp6BBB0UnfRYP9NP4FANqUZYb0hnfs3TM3hw==", + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.1", + "@csstools/css-color-parser": "^3.0.7", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, "node_modules/@babel/runtime": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz", - "integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==", + "version": "7.26.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.7.tgz", + "integrity": "sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==", + "license": "MIT", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -54,6 +78,116 @@ "node": ">=6.9.0" } }, + "node_modules/@csstools/color-helpers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.0.1.tgz", + "integrity": "sha512-MKtmkA0BX87PKaO1NFRTFH+UnkgnmySQOvNxJubsadusqPEC2aJ9MOQiMceZJJ6oitUl/i0L6u0M1IrmAOmgBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.1.tgz", + "integrity": "sha512-rL7kaUnTkL9K+Cvo2pnCieqNpTKgQzy5f+N+5Iuko9HAoasP+xgprVh7KN/MaJVvVL1l0EzQq2MoqBHKSrDrag==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.0.7.tgz", + "integrity": "sha512-nkMp2mTICw32uE5NN+EsJ4f5N+IGFeCFu4bGpiKgb2Pq/7J/MpyLBeQ5ry4KKtRFZaYs6sTmcMYrSRIyj5DFKA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.0.1", + "@csstools/css-calc": "^2.1.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.4.tgz", + "integrity": "sha512-Up7rBoV77rv29d3uKHUIVubz1BTcgyUK72IvCQAbfbMv584xHcGKCKbWh7i8hPrRJ7qU4Y8IO3IY9m+iTB7P3A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.3" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.3.tgz", + "integrity": "sha512-UJnjoFsmxfKUdNYdWgOB0mWUypuLvAfQPH1+pyvRJs6euowbFkFC6P13w1l8mJyi3vxYMxc9kld5jZEGRQs6bw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@emnapi/runtime": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", @@ -107,13 +241,13 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.1.tgz", - "integrity": "sha512-fo6Mtm5mWyKjA/Chy1BYTdn5mGJoDNjC7C64ug20ADsRDGrA85bN3uK3MaKbeRkRuuIEAR5N33Jr1pbm411/PA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.5", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -121,10 +255,20 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-helpers": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.1.0.tgz", + "integrity": "sha512-kLrdPDJE1ckPo94kmPPf9Hfd0DU0Jw6oKYrhe+pwSC0iTUInmTa+w6fw8sGgcfkFJGNdWOUeOaDM4quW4a7OkA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -135,9 +279,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.0.tgz", + "integrity": "sha512-yaVPAiNAalnCZedKLdR21GOGILMLKPyqSLWaAjQFvYA2i/ciDi8ArYVr69Anohb6cH2Ukhqti4aFnYyPm8wdwQ==", "dev": true, "license": "MIT", "dependencies": { @@ -159,9 +303,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.18.0.tgz", - "integrity": "sha512-fK6L7rxcq6/z+AaQMtiFTkvbHkBLNlwyRxHpKawP0x3u9+NC6MQTnFW+AdpwC6gfHTW0051cokQgtTN2FqlxQA==", + "version": "9.22.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.22.0.tgz", + "integrity": "sha512-vLFajx9o8d1/oL2ZkpMYbkLv8nDB6yaIwFNt7nI4+I80U/z03SxmfOMsLbvWr3p7C+Wnoh//aOu2pQW8cS0HCQ==", "dev": true, "license": "MIT", "engines": { @@ -169,9 +313,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.5.tgz", - "integrity": "sha512-o0bhxnL89h5Bae5T318nFoFzGy+YE5i/gGkoPAgkmTVdRKTiv3p8JHevPiPaMwoloKfEiiaHlawCqaZMqRm+XQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -179,13 +323,13 @@ } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.5.tgz", - "integrity": "sha512-lB05FkqEdUg2AA0xEbUz0SnkXT1LcCTa438W4IWTUh4hdOnVbQyOJ81OrDXsJk/LSiJHubgGEFoR5EHq1NsH1A==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/core": "^0.10.0", + "@eslint/core": "^0.12.0", "levn": "^0.4.1" }, "engines": { @@ -196,6 +340,7 @@ "version": "1.6.9", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", + "license": "MIT", "dependencies": { "@floating-ui/utils": "^0.2.9" } @@ -204,6 +349,7 @@ "version": "1.6.13", "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", + "license": "MIT", "dependencies": { "@floating-ui/core": "^1.6.0", "@floating-ui/utils": "^0.2.9" @@ -213,6 +359,7 @@ "version": "0.26.28", "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.28.tgz", "integrity": "sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==", + "license": "MIT", "dependencies": { "@floating-ui/react-dom": "^2.1.2", "@floating-ui/utils": "^0.2.8", @@ -227,6 +374,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", + "license": "MIT", "dependencies": { "@floating-ui/dom": "^1.0.0" }, @@ -238,7 +386,8 @@ "node_modules/@floating-ui/utils": { "version": "0.2.9", "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", - "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==", + "license": "MIT" }, "node_modules/@humanfs/core": { "version": "0.19.1", @@ -293,9 +442,9 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -671,7 +820,6 @@ "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -689,7 +837,6 @@ "version": "0.3.8", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/set-array": "^1.2.1", @@ -704,7 +851,6 @@ "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -714,7 +860,6 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" @@ -724,14 +869,12 @@ "version": "1.5.0", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", - "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.25", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", @@ -739,9 +882,10 @@ } }, "node_modules/@mantine/core": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.16.1.tgz", - "integrity": "sha512-HYdjCeMU3dUJbc1CrAAedeAASTG5kVyL/qsiuYh5b7BoG0qsRtK8WJxBpUjW6VqtJpUaE94c5tlBJ8MgAmPHTQ==", + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@mantine/core/-/core-7.17.1.tgz", + "integrity": "sha512-V8O3Ftq4la4I4wNDkTfH4Slkt/pCEU32pTE/DkO46zua0VFxfOAJeLjaol0s11//T+bXx82DtjMsd9APWPuFhA==", + "license": "MIT", "dependencies": { "@floating-ui/react": "^0.26.28", "clsx": "^2.1.1", @@ -751,29 +895,64 @@ "type-fest": "^4.27.0" }, "peerDependencies": { - "@mantine/hooks": "7.16.1", + "@mantine/hooks": "7.17.1", "react": "^18.x || ^19.x", "react-dom": "^18.x || ^19.x" } }, "node_modules/@mantine/hooks": { - "version": "7.16.1", - "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.16.1.tgz", - "integrity": "sha512-+hER8E4d2ByfQ/DKIXGM3Euxb7IH5ArSjzzzoF21sG095iXIryOCob22ZanrmiXCoAzKKdxqgVj4Di67ikLYSQ==", + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@mantine/hooks/-/hooks-7.17.1.tgz", + "integrity": "sha512-mkHLrXMPd5xdI5WD7UOLwNEpdh/i6A7HaRDTXvjDE2/S0N8VmAE+BlvdyvWRMi7ODp2zVqJdP8cF1tgUn+Z0fA==", + "license": "MIT", "peerDependencies": { "react": "^18.x || ^19.x" } }, + "node_modules/@mantine/notifications": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@mantine/notifications/-/notifications-7.17.1.tgz", + "integrity": "sha512-jsCNkkjgtsGYIMbCrzBY0UBckoXyeaSWbEoJdvMlfA+LaeOQrSLxa+ot+1+wPaoZxR+1Q1xOwC1X5bTxHKudBA==", + "license": "MIT", + "dependencies": { + "@mantine/store": "7.17.1", + "react-transition-group": "4.4.5" + }, + "peerDependencies": { + "@mantine/core": "7.17.1", + "@mantine/hooks": "7.17.1", + "react": "^18.x || ^19.x", + "react-dom": "^18.x || ^19.x" + } + }, + "node_modules/@mantine/store": { + "version": "7.17.1", + "resolved": "https://registry.npmjs.org/@mantine/store/-/store-7.17.1.tgz", + "integrity": "sha512-is1c0FycakMsbTElKGWO59LarjMIk24JUXfjP/QIrB0XqpXreq3u7aN4hoNqr1IftTZSfVBii4W8pVFeWaV55g==", + "license": "MIT", + "peerDependencies": { + "react": "^18.x || ^19.x" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.9.tgz", + "integrity": "sha512-tVkljjeEaAhCqTzajSdgbQ6gE6f3oneVwa3iXR6csiEwXXOFsiC6Uh9iAjAhXPtqa/XMDHWjjeNH/77m/Yq2dw==", + "license": "MIT", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, "node_modules/@next/env": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.5.tgz", - "integrity": "sha512-jg8ygVq99W3/XXb9Y6UQsritwhjc+qeiO7QrGZRYOfviyr/HcdnhdBQu4gbp2rBIh2ZyBYTBMWbPw3JSCb0GHw==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.7.tgz", + "integrity": "sha512-d9jnRrkuOH7Mhi+LHav2XW91HOgTAWHxjMPkXMGBc9B2b7614P7kjt8tAplRvJpbSt4nbO1lugcT/kAaWzjlLQ==", "license": "MIT" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.1.5.tgz", - "integrity": "sha512-3cCrXBybsqe94UxD6DBQCYCCiP9YohBMgZ5IzzPYHmPzj8oqNlhBii5b6o1HDDaRHdz2pVnSsAROCtrczy8O0g==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.1.7.tgz", + "integrity": "sha512-kRP7RjSxfTO13NE317ek3mSGzoZlI33nc/i5hs1KaWpK+egs85xg0DJ4p32QEiHnR0mVjuUfhRIun7awqfL7pQ==", "dev": true, "license": "MIT", "dependencies": { @@ -781,9 +960,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.5.tgz", - "integrity": "sha512-5ttHGE75Nw9/l5S8zR2xEwR8OHEqcpPym3idIMAZ2yo+Edk0W/Vf46jGqPOZDk+m/SJ+vYZDSuztzhVha8rcdA==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.7.tgz", + "integrity": "sha512-hPFwzPJDpA8FGj7IKV3Yf1web3oz2YsR8du4amKw8d+jAOHfYHYFpMkoF6vgSY4W6vB29RtZEklK9ayinGiCmQ==", "cpu": [ "arm64" ], @@ -797,9 +976,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.5.tgz", - "integrity": "sha512-8YnZn7vDURUUTInfOcU5l0UWplZGBqUlzvqKKUFceM11SzfNEz7E28E1Arn4/FsOf90b1Nopboy7i7ufc4jXag==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.7.tgz", + "integrity": "sha512-2qoas+fO3OQKkU0PBUfwTiw/EYpN+kdAx62cePRyY1LqKtP09Vp5UcUntfZYajop5fDFTjSxCHfZVRxzi+9FYQ==", "cpu": [ "x64" ], @@ -813,9 +992,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.5.tgz", - "integrity": "sha512-rDJC4ctlYbK27tCyFUhgIv8o7miHNlpCjb2XXfTLQszwAUOSbcMN9q2y3urSrrRCyGVOd9ZR9a4S45dRh6JF3A==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.7.tgz", + "integrity": "sha512-sKLLwDX709mPdzxMnRIXLIT9zaX2w0GUlkLYQnKGoXeWUhcvpCrK+yevcwCJPdTdxZEUA0mOXGLdPsGkudGdnA==", "cpu": [ "arm64" ], @@ -829,9 +1008,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.5.tgz", - "integrity": "sha512-FG5RApf4Gu+J+pHUQxXPM81oORZrKBYKUaBTylEIQ6Lz17hKVDsLbSXInfXM0giclvXbyiLXjTv42sQMATmZ0A==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.7.tgz", + "integrity": "sha512-zblK1OQbQWdC8fxdX4fpsHDw+VSpBPGEUX4PhSE9hkaWPrWoeIJn+baX53vbsbDRaDKd7bBNcXRovY1hEhFd7w==", "cpu": [ "arm64" ], @@ -845,9 +1024,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.5.tgz", - "integrity": "sha512-NX2Ar3BCquAOYpnoYNcKz14eH03XuF7SmSlPzTSSU4PJe7+gelAjxo3Y7F2m8+hLT8ZkkqElawBp7SWBdzwqQw==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.7.tgz", + "integrity": "sha512-GOzXutxuLvLHFDAPsMP2zDBMl1vfUHHpdNpFGhxu90jEzH6nNIgmtw/s1MDwpTOiM+MT5V8+I1hmVFeAUhkbgQ==", "cpu": [ "x64" ], @@ -861,9 +1040,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.5.tgz", - "integrity": "sha512-EQgqMiNu3mrV5eQHOIgeuh6GB5UU57tu17iFnLfBEhYfiOfyK+vleYKh2dkRVkV6ayx3eSqbIYgE7J7na4hhcA==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.7.tgz", + "integrity": "sha512-WrZ7jBhR7ATW1z5iEQ0ZJfE2twCNSXbpCSaAunF3BKcVeHFADSI/AW1y5Xt3DzTqPF1FzQlwQTewqetAABhZRQ==", "cpu": [ "x64" ], @@ -877,9 +1056,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.5.tgz", - "integrity": "sha512-HPULzqR/VqryQZbZME8HJE3jNFmTGcp+uRMHabFbQl63TtDPm+oCXAz3q8XyGv2AoihwNApVlur9Up7rXWRcjg==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.7.tgz", + "integrity": "sha512-LDnj1f3OVbou1BqvvXVqouJZKcwq++mV2F+oFHptToZtScIEnhNRJAhJzqAtTE2dB31qDYL45xJwrc+bLeKM2Q==", "cpu": [ "arm64" ], @@ -893,9 +1072,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.5.tgz", - "integrity": "sha512-n74fUb/Ka1dZSVYfjwQ+nSJ+ifUff7jGurFcTuJNKZmI62FFOxQXUYit/uZXPTj2cirm1rvGWHG2GhbSol5Ikw==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.7.tgz", + "integrity": "sha512-dC01f1quuf97viOfW05/K8XYv2iuBgAxJZl7mbCKEjMgdQl5JjAKJ0D2qMKZCgPWDeFbFT0Q0nYWwytEW0DWTQ==", "cpu": [ "x64" ], @@ -908,11 +1087,23 @@ "node": ">= 10" } }, + "node_modules/@next/third-parties": { + "version": "15.2.0", + "resolved": "https://registry.npmjs.org/@next/third-parties/-/third-parties-15.2.0.tgz", + "integrity": "sha512-Nu1qNgMHKK4Uz1nzFio5xNj8dgSuGhcm3mCoVj2Qz5peHRVuhxADP7y+KYDvkb2z2JkZ+REllQ1ZwrqXg8BnQg==", + "license": "MIT", + "dependencies": { + "third-party-capital": "1.0.20" + }, + "peerDependencies": { + "next": "^13.0.0 || ^14.0.0 || ^15.0.0", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "2.0.5", @@ -926,7 +1117,6 @@ "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -936,7 +1126,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", @@ -960,7 +1149,6 @@ "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, "license": "MIT", "optional": true, "engines": { @@ -997,20 +1185,22 @@ } }, "node_modules/@tabler/icons": { - "version": "3.28.1", - "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.28.1.tgz", - "integrity": "sha512-h7nqKEvFooLtFxhMOC1/2eiV+KRXhBUuDUUJrJlt6Ft6tuMw2eU/9GLQgrTk41DNmIEzp/LI83K9J9UUU8YBYQ==", + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/@tabler/icons/-/icons-3.30.0.tgz", + "integrity": "sha512-c8OKLM48l00u9TFbh2qhSODMONIzML8ajtCyq95rW8vzkWcBrKRPM61tdkThz2j4kd5u17srPGIjqdeRUZdfdw==", + "license": "MIT", "funding": { "type": "github", "url": "https://github.com/sponsors/codecalm" } }, "node_modules/@tabler/icons-react": { - "version": "3.28.1", - "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.28.1.tgz", - "integrity": "sha512-KNBpA2kbxr3/2YK5swt7b/kd/xpDP1FHYZCxDFIw54tX8slELRFEf95VMxsccQHZeIcUbdoojmUUuYSbt/sM5Q==", + "version": "3.30.0", + "resolved": "https://registry.npmjs.org/@tabler/icons-react/-/icons-react-3.30.0.tgz", + "integrity": "sha512-9KZ9D1UNAyjlLkkYp2HBPHdf6lAJ2aelDqh8YYAnnmLF3xwprWKxxW8+zw5jlI0IwdfN4XFFuzqePkaw+DpIOg==", + "license": "MIT", "dependencies": { - "@tabler/icons": "3.28.1" + "@tabler/icons": "3.30.0" }, "funding": { "type": "github", @@ -1020,6 +1210,34 @@ "react": ">= 16" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.16.tgz", + "integrity": "sha512-0wDLwCVF5V3x3b1SGXPCDcdsbDHMBe+lkFzBRaHeLvNi+nrrnZ1lA18u+OTWO8iSWU2GxUOCvlXtDuqftc1oiA==", + "license": "MIT", + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tailwindcss/typography/node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/@types/estree": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", @@ -1042,19 +1260,19 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.17.14", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.17.14.tgz", - "integrity": "sha512-w6qdYetNL5KRBiSClK/KWai+2IMEJuAj+EujKCumalFOwXtvOXaEan9AuwcRID2IcOIAWSIfR495hBtgKlx2zg==", + "version": "22.13.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", + "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", "dev": true, "license": "MIT", "dependencies": { - "undici-types": "~6.19.2" + "undici-types": "~6.20.0" } }, "node_modules/@types/react": { - "version": "19.0.7", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.7.tgz", - "integrity": "sha512-MoFsEJKkAtZCrC1r6CM8U22GzhG7u2Wir8ons/aCKH6MBdD1ibV24zOSSkdZVUKqN5i396zG5VKLYZ3yaUZdLA==", + "version": "19.0.8", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.0.8.tgz", + "integrity": "sha512-9P/o1IGdfmQxrujGbIMDyYaaCykhLKc0NGCtYcECNUr9UAaDe4gwvV9bR6tvd5Br1SG0j+PBpbKr2UYY8CwqSw==", "devOptional": true, "license": "MIT", "dependencies": { @@ -1071,18 +1289,40 @@ "@types/react": "^19.0.0" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==", + "license": "MIT" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.5", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.5.tgz", + "integrity": "sha512-coYR071JRaHa+xoEvvYqvnIHaVqaYrLPbsufM9BF63HkwI5Lgmy2QR8Q5K/lYDYo5AK82wOvSOS0UsLTpTG7uQ==", + "license": "MIT", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.20.0.tgz", - "integrity": "sha512-naduuphVw5StFfqp4Gq4WhIBE2gN1GEmMUExpJYknZJdRnc+2gDzB8Z3+5+/Kv33hPQRDGzQO/0opHE72lZZ6A==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.22.0.tgz", + "integrity": "sha512-4Uta6REnz/xEJMvwf72wdUnC3rr4jAQf5jnTkeRQ9b6soxLxhDEbS/pfMPoJLDfFPNVRdryqWUIV/2GZzDJFZw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/type-utils": "8.20.0", - "@typescript-eslint/utils": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/type-utils": "8.22.0", + "@typescript-eslint/utils": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", @@ -1102,16 +1342,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.20.0.tgz", - "integrity": "sha512-gKXG7A5HMyjDIedBi6bUrDcun8GIjnI8qOwVLiY3rx6T/sHP/19XLJOnIq/FgQvWLHja5JN/LSE7eklNBr612g==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.22.0.tgz", + "integrity": "sha512-MqtmbdNEdoNxTPzpWiWnqNac54h8JDAmkWtJExBVVnSrSmi9z+sZUt0LfKqk9rjqmKOIeRhO4fHHJ1nQIjduIQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4" }, "engines": { @@ -1127,14 +1367,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.20.0.tgz", - "integrity": "sha512-J7+VkpeGzhOt3FeG1+SzhiMj9NzGD/M6KoGn9f4dbz3YzK9hvbhVTmLj/HiTp9DazIzJ8B4XcM80LrR9Dm1rJw==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.22.0.tgz", + "integrity": "sha512-/lwVV0UYgkj7wPSw0o8URy6YI64QmcOdwHuGuxWIYznO6d45ER0wXUbksr9pYdViAofpUCNJx/tAzNukgvaaiQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0" + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1145,14 +1385,14 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.20.0.tgz", - "integrity": "sha512-bPC+j71GGvA7rVNAHAtOjbVXbLN5PkwqMvy1cwGeaxUoRQXVuKCebRoLzm+IPW/NtFFpstn1ummSIasD5t60GA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.22.0.tgz", + "integrity": "sha512-NzE3aB62fDEaGjaAYZE4LH7I1MUwHooQ98Byq0G0y3kkibPJQIXVUspzlFOmOfHhiDLwKzMlWxaNv+/qcZurJA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/typescript-estree": "8.20.0", - "@typescript-eslint/utils": "8.20.0", + "@typescript-eslint/typescript-estree": "8.22.0", + "@typescript-eslint/utils": "8.22.0", "debug": "^4.3.4", "ts-api-utils": "^2.0.0" }, @@ -1169,9 +1409,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.20.0.tgz", - "integrity": "sha512-cqaMiY72CkP+2xZRrFt3ExRBu0WmVitN/rYPZErA80mHjHx/Svgp8yfbzkJmDoQ/whcytOPO9/IZXnOc+wigRA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.22.0.tgz", + "integrity": "sha512-0S4M4baNzp612zwpD4YOieP3VowOARgK2EkN/GBn95hpyF8E2fbMT55sRHWBq+Huaqk3b3XK+rxxlM8sPgGM6A==", "dev": true, "license": "MIT", "engines": { @@ -1183,14 +1423,14 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.20.0.tgz", - "integrity": "sha512-Y7ncuy78bJqHI35NwzWol8E0X7XkRVS4K4P4TCyzWkOJih5NDvtoRDW4Ba9YJJoB2igm9yXDdYI/+fkiiAxPzA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.22.0.tgz", + "integrity": "sha512-SJX99NAS2ugGOzpyhMza/tX+zDwjvwAtQFLsBo3GQxiGcvaKlqGBkmZ+Y1IdiSi9h4Q0Lr5ey+Cp9CGWNY/F/w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/visitor-keys": "8.20.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/visitor-keys": "8.22.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -1266,16 +1506,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.20.0.tgz", - "integrity": "sha512-dq70RUw6UK9ei7vxc4KQtBRk7qkHZv447OUZ6RPQMQl71I3NZxQJX/f32Smr+iqWrB02pHKn2yAdHBb0KNrRMA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.22.0.tgz", + "integrity": "sha512-T8oc1MbF8L+Bk2msAvCUzjxVB2Z2f+vXYfcucE2wOmYs7ZUwco5Ep0fYZw8quNwOiw9K8GYVL+Kgc2pETNTLOg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.20.0", - "@typescript-eslint/types": "8.20.0", - "@typescript-eslint/typescript-estree": "8.20.0" + "@typescript-eslint/scope-manager": "8.22.0", + "@typescript-eslint/types": "8.22.0", + "@typescript-eslint/typescript-estree": "8.22.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -1290,13 +1530,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.20.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.20.0.tgz", - "integrity": "sha512-v/BpkeeYAsPkKCkR8BDwcno0llhzWVqPOamQrAEMdpZav2Y9OVjd9dwJyBLJWwf335B5DmlifECIkZRJCaGaHA==", + "version": "8.22.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.22.0.tgz", + "integrity": "sha512-AWpYAXnUgvLNabGTy3uBylkgZoosva/miNd1I8Bz3SjotmQPbVqhO4Cczo8AsZ44XVErEBPr/CRSgaj8sG7g0w==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.20.0", + "@typescript-eslint/types": "8.22.0", "eslint-visitor-keys": "^4.2.0" }, "engines": { @@ -1307,6 +1547,79 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@vercel/analytics": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@vercel/analytics/-/analytics-1.5.0.tgz", + "integrity": "sha512-MYsBzfPki4gthY5HnYN7jgInhAZ7Ac1cYDoRWFomwGHWEX7odTEzbtg9kf/QSo7XEsEAqlQugA6gJ2WS2DEa3g==", + "license": "MPL-2.0", + "peerDependencies": { + "@remix-run/react": "^2", + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@remix-run/react": { + "optional": true + }, + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, + "node_modules/@vercel/speed-insights": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@vercel/speed-insights/-/speed-insights-1.2.0.tgz", + "integrity": "sha512-y9GVzrUJ2xmgtQlzFP2KhVRoCglwfRQgjyfY607aU0hh0Un6d0OUyrJkjuAlsV18qR4zfoFPs/BiIj9YDS6Wzw==", + "hasInstallScript": true, + "license": "Apache-2.0", + "peerDependencies": { + "@sveltejs/kit": "^1 || ^2", + "next": ">= 13", + "react": "^18 || ^19 || ^19.0.0-rc", + "svelte": ">= 4", + "vue": "^3", + "vue-router": "^4" + }, + "peerDependenciesMeta": { + "@sveltejs/kit": { + "optional": true + }, + "next": { + "optional": true + }, + "react": { + "optional": true + }, + "svelte": { + "optional": true + }, + "vue": { + "optional": true + }, + "vue-router": { + "optional": true + } + } + }, "node_modules/acorn": { "version": "8.14.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", @@ -1330,6 +1643,15 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1351,7 +1673,6 @@ "version": "6.1.0", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -1364,7 +1685,6 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1380,14 +1700,12 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, "license": "MIT" }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", @@ -1401,7 +1719,6 @@ "version": "5.0.2", "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -1585,6 +1902,22 @@ "dev": true, "license": "MIT" }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -1625,14 +1958,12 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, "license": "MIT" }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -1656,7 +1987,6 @@ "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, "license": "MIT", "dependencies": { "fill-range": "^7.1.1" @@ -1665,6 +1995,15 @@ "node": ">=8" } }, + "node_modules/bson": { + "version": "6.10.3", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.10.3.tgz", + "integrity": "sha512-MTxGsqgYTwfshYWTRdmZRC+M7FnG1b4y7RO7p2k3X24Wq0yv1m77Wsj0BzlPzd/IowgESfsruQCUToa7vbOpPQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=16.20.1" + } + }, "node_modules/busboy": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", @@ -1740,16 +2079,15 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001692", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001692.tgz", - "integrity": "sha512-A95VKan0kdtrsnMubMKxEKUKImOPSuCpYgxSQBo036P5YYgVIcOYJEgt/txJWqObiRQeISNCfef9nvlQ0vbV7A==", + "version": "1.0.30001695", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001695.tgz", + "integrity": "sha512-vHyLade6wTgI2u1ec3WQBxv+2BrTERV28UXQu9LO6lZ9pYeMk34vjXFLOxo1A4UBA8XTL4njRQZdno/yYaSmWw==", "funding": [ { "type": "opencollective", @@ -1787,7 +2125,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, "license": "MIT", "dependencies": { "anymatch": "~3.1.2", @@ -1812,7 +2149,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -1831,6 +2167,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -1853,7 +2190,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "devOptional": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1866,7 +2202,6 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "devOptional": true, "license": "MIT" }, "node_modules/color-string": { @@ -1880,11 +2215,22 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/commander": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -1901,7 +2247,6 @@ "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", @@ -1916,7 +2261,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -1925,11 +2269,23 @@ "node": ">=4" } }, + "node_modules/cssstyle": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.2.1.tgz", + "integrity": "sha512-9+vem03dMXG7gDmZ62uqmRiMRNtinIZ9ZyuF6BdxzfOD+FdN5hretzynkn0ReS2DO2GSw76RWHs0UmJPI2zUjw==", + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^2.8.2", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, "license": "MIT" }, "node_modules/damerau-levenshtein": { @@ -1939,6 +2295,19 @@ "dev": true, "license": "BSD-2-Clause" }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/data-view-buffer": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", @@ -1997,7 +2366,6 @@ "version": "4.4.0", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", - "dev": true, "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -2011,6 +2379,12 @@ } } }, + "node_modules/decimal.js": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.5.0.tgz", + "integrity": "sha512-8vDa8Qxvr/+d94hSh5P3IJwI5t8/c0KsMp+g8bNw9cY2icONa5aPfvKeieW1WlG0WQYwwhJ7mjui2xtiePQSXw==", + "license": "MIT" + }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -2054,6 +2428,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -2067,20 +2450,19 @@ "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", + "license": "MIT" }, "node_modules/didyoumean": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, "license": "Apache-2.0" }, "node_modules/dlv": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, "license": "MIT" }, "node_modules/doctrine": { @@ -2096,6 +2478,25 @@ "node": ">=0.10.0" } }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dompurify": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", + "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2115,14 +2516,12 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, "license": "MIT" }, "node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, "license": "MIT" }, "node_modules/enhanced-resolve": { @@ -2139,6 +2538,18 @@ "node": ">=10.13.0" } }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-abstract": { "version": "1.23.9", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", @@ -2324,22 +2735,23 @@ } }, "node_modules/eslint": { - "version": "9.18.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.18.0.tgz", - "integrity": "sha512-+waTfRWQlSbpt3KWE+CjrPPYnbq9kfZIYUqapc0uBXyjTp8aYXZDsUH16m39Ryq3NjAVP4tjuF7KaukeqoCoaA==", + "version": "9.22.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.22.0.tgz", + "integrity": "sha512-9V/QURhsRN40xuHXWjV64yvrzMjcz7ZyNoF2jJFmy9j/SLk0u1OLSZgXi28MrXjymnjEGSR80WCdab3RGMDveQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.10.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.18.0", - "@eslint/plugin-kit": "^0.2.5", + "@eslint/config-array": "^0.19.2", + "@eslint/config-helpers": "^0.1.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.0", + "@eslint/js": "9.22.0", + "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", @@ -2347,7 +2759,7 @@ "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", + "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", @@ -2384,13 +2796,13 @@ } }, "node_modules/eslint-config-next": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.1.5.tgz", - "integrity": "sha512-Awm7iUJY8toOR+fU8yTxZnA7/LyOGUGOd6cENCuDfJ3gucHOSmLdOSGJ4u+nlrs8p5qXemua42bZmq+uOzxl6Q==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.1.7.tgz", + "integrity": "sha512-zXoMnYUIy3XHaAoOhrcYkT9UQWvXqWju2K7NNsmb5wd/7XESDwof61eUdW4QhERr3eJ9Ko/vnXqIrj8kk/drYw==", "dev": true, "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.1.5", + "@next/eslint-plugin-next": "15.1.7", "@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", @@ -2686,9 +3098,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -2834,7 +3246,6 @@ "version": "1.18.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.18.0.tgz", "integrity": "sha512-QKHXPW0hD8g4UET03SdOdunzSouc9N4AuHdsX8XNcTsuz+yYFILVNIX4l9yHABMhiEI9Db0JTTIpu0wB+Y1QQw==", - "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" @@ -2857,7 +3268,6 @@ "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" @@ -2905,20 +3315,25 @@ "license": "ISC" }, "node_modules/for-each": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", - "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.4.tgz", + "integrity": "sha512-kKaIINnFpzW6ffJNDjjyjrk21BkDx38c0xa/klsT8VzLCaMEefv4ZTacrcVR4DmgTeBra++jMDAfS/tS799YDw==", "dev": true, "license": "MIT", "dependencies": { - "is-callable": "^1.1.3" + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, "node_modules/foreground-child": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", - "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -2931,11 +3346,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/form-data": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", + "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, @@ -2950,7 +3378,6 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3016,6 +3443,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", + "license": "MIT", "engines": { "node": ">=6" } @@ -3053,9 +3481,9 @@ } }, "node_modules/get-tsconfig": { - "version": "4.8.1", - "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.8.1.tgz", - "integrity": "sha512-k9PN+cFBmaLWtVz29SkUoqU5O0slLuHJXt/2P+tMVFT+phsSGXGkp9t3rQIqdz0e+06EHNGs3oM6ZX1s2zHxRg==", + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", + "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", "dev": true, "license": "MIT", "dependencies": { @@ -3069,7 +3497,6 @@ "version": "10.4.5", "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -3090,7 +3517,6 @@ "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" @@ -3103,7 +3529,6 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -3113,7 +3538,6 @@ "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -3267,7 +3691,6 @@ "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "dev": true, "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3276,6 +3699,56 @@ "node": ">= 0.4" } }, + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", @@ -3354,12 +3827,13 @@ "optional": true }, "node_modules/is-async-function": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.0.tgz", - "integrity": "sha512-GExz9MtyhlZyXYLxzlJRj5WUCE661zhDa1Yna52CN57AJsymh+DvXXjyveSioqSRdxvUrdKdvqB1b5cVKsNpWQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", "call-bound": "^1.0.3", "get-proto": "^1.0.1", "has-tostringtag": "^1.0.2", @@ -3392,7 +3866,6 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -3445,7 +3918,6 @@ "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" @@ -3496,7 +3968,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -3522,7 +3993,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -3551,7 +4021,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -3577,7 +4046,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -3600,6 +4068,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "license": "MIT" + }, "node_modules/is-regex": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", @@ -3756,9 +4230,21 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, "license": "ISC" }, + "node_modules/isomorphic-dompurify": { + "version": "2.22.0", + "resolved": "https://registry.npmjs.org/isomorphic-dompurify/-/isomorphic-dompurify-2.22.0.tgz", + "integrity": "sha512-A2xsDNST1yB94rErEnwqlzSvGllCJ4e8lDMe1OWBH2hvpfc/2qzgMEiDshTO1HwO+PIDTiYeOc7ZDB7Ds49BOg==", + "license": "MIT", + "dependencies": { + "dompurify": "^3.2.4", + "jsdom": "^26.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/iterator.prototype": { "version": "1.1.5", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", @@ -3781,7 +4267,6 @@ "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -3797,7 +4282,6 @@ "version": "1.21.7", "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, "license": "MIT", "bin": { "jiti": "bin/jiti.js" @@ -3807,7 +4291,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -3819,8 +4302,48 @@ "dependencies": { "argparse": "^2.0.1" }, - "bin": { - "js-yaml": "bin/js-yaml.js" + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "26.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-26.0.0.tgz", + "integrity": "sha512-BZYDGVAIriBWTpIxYzrXjv3E/4u8+/pSG5bQdIYCbNCGOvsPkDQfTVLAIXAf9ETdCpduCVTkDe2NNZ8NIwUVzw==", + "license": "MIT", + "dependencies": { + "cssstyle": "^4.2.1", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.1", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.16", + "parse5": "^7.2.1", + "rrweb-cssom": "^0.8.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.1.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } } }, "node_modules/json-buffer": { @@ -3921,7 +4444,6 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -3934,7 +4456,6 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, "license": "MIT" }, "node_modules/locate-path": { @@ -3953,18 +4474,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "license": "MIT" + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, "license": "MIT" }, "node_modules/loose-envify": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "dev": true, "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" @@ -3977,7 +4508,6 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, "license": "ISC" }, "node_modules/math-intrinsics": { @@ -3990,11 +4520,16 @@ "node": ">= 0.4" } }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==", + "license": "MIT" + }, "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 8" @@ -4004,7 +4539,6 @@ "version": "4.0.8", "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, "license": "MIT", "dependencies": { "braces": "^3.0.3", @@ -4014,6 +4548,27 @@ "node": ">=8.6" } }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -4041,24 +4596,77 @@ "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } }, + "node_modules/mongodb": { + "version": "6.14.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.14.2.tgz", + "integrity": "sha512-kMEHNo0F3P6QKDq17zcDuPeaywK/YaJVCEQRzPF3TOM/Bl9MFg64YE5Tu7ifj37qZJMhwU1tl2Ioivws5gRG5Q==", + "license": "Apache-2.0", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.9", + "bson": "^6.10.3", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0 || ^2.0.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.2.tgz", + "integrity": "sha512-rMO7CGo/9BFwyZABcKAWL8UJwH/Kc2x0g72uhDWzG48URRax5TCIcJ7Rc3RZqffZzO/Gwff/jyKwCU9TN8gehA==", + "license": "Apache-2.0", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^14.1.0 || ^13.0.0" + } + }, "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==", - "dev": true, "license": "MIT" }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0", @@ -4092,12 +4700,12 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.1.5", - "resolved": "https://registry.npmjs.org/next/-/next-15.1.5.tgz", - "integrity": "sha512-Cf/TEegnt01hn3Hoywh6N8fvkhbOuChO4wFje24+a86wKOubgVaWkDqxGVgoWlz2Hp9luMJ9zw3epftujdnUOg==", + "version": "15.1.7", + "resolved": "https://registry.npmjs.org/next/-/next-15.1.7.tgz", + "integrity": "sha512-GNeINPGS9c6OZKCvKypbL8GTsT5GhWPp4DM0fzkXJuXMilOO2EeFxuAY6JZbtk6XIl6Ws10ag3xRINDjSO5+wg==", "license": "MIT", "dependencies": { - "@next/env": "15.1.5", + "@next/env": "15.1.7", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -4112,14 +4720,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.1.5", - "@next/swc-darwin-x64": "15.1.5", - "@next/swc-linux-arm64-gnu": "15.1.5", - "@next/swc-linux-arm64-musl": "15.1.5", - "@next/swc-linux-x64-gnu": "15.1.5", - "@next/swc-linux-x64-musl": "15.1.5", - "@next/swc-win32-arm64-msvc": "15.1.5", - "@next/swc-win32-x64-msvc": "15.1.5", + "@next/swc-darwin-arm64": "15.1.7", + "@next/swc-darwin-x64": "15.1.7", + "@next/swc-linux-arm64-gnu": "15.1.7", + "@next/swc-linux-arm64-musl": "15.1.7", + "@next/swc-linux-x64-gnu": "15.1.7", + "@next/swc-linux-x64-musl": "15.1.7", + "@next/swc-win32-arm64-msvc": "15.1.7", + "@next/swc-win32-x64-msvc": "15.1.7", "sharp": "^0.33.5" }, "peerDependencies": { @@ -4177,17 +4785,21 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/nwsapi": { + "version": "2.2.16", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.16.tgz", + "integrity": "sha512-F1I/bimDpj3ncaNDhfyMWuFqmQDBwDB0Fogc2qpL3BWvkQteFD/8BzWuIRl83rq0DXfm8SGt/HFhLXZyljTXcQ==", + "license": "MIT" + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4197,7 +4809,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -4387,7 +4998,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/parent-module": { @@ -4403,6 +5013,18 @@ "node": ">=6" } }, + "node_modules/parse5": { + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.2.1.tgz", + "integrity": "sha512-BuBYQYlv1ckiPdQi/ohiivi9Sagc9JG+Ozs0r7b/0iK3sKmrb0b9FdWdBbOdx6hBCM/F9Ir82ofnBhtZOjCRPQ==", + "license": "MIT", + "dependencies": { + "entities": "^4.5.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -4417,7 +5039,6 @@ "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -4427,14 +5048,12 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -4457,7 +5076,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, "license": "MIT", "engines": { "node": ">=8.6" @@ -4470,7 +5088,6 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -4480,7 +5097,6 @@ "version": "4.0.6", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", - "dev": true, "license": "MIT", "engines": { "node": ">= 6" @@ -4497,10 +5113,9 @@ } }, "node_modules/postcss": { - "version": "8.5.1", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.1.tgz", - "integrity": "sha512-6oz2beyjc5VMn/KV1pPw8fliQkhBXrVn1Z3TVyqZxU8kZpzEKhBdmCFqI6ZbmGtamQvQGuU1sgPTk8ZrXDD7jQ==", - "dev": true, + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "funding": [ { "type": "opencollective", @@ -4515,6 +5130,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -4528,7 +5144,6 @@ "version": "15.1.0", "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, "license": "MIT", "dependencies": { "postcss-value-parser": "^4.0.0", @@ -4546,7 +5161,6 @@ "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz", "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==", - "dev": true, "license": "MIT", "dependencies": { "camelcase-css": "^2.0.1" @@ -4566,7 +5180,6 @@ "version": "4.0.2", "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz", "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4603,6 +5216,7 @@ "resolved": "https://registry.npmjs.org/postcss-mixins/-/postcss-mixins-9.0.4.tgz", "integrity": "sha512-XVq5jwQJDRu5M1XGkdpgASqLk37OqkH4JCFDXl/Dn7janOJjCTEKL+36cnRVy7bMtoBzALfO7bV7nTIsFnUWLA==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "^3.2.11", "postcss-js": "^4.0.0", @@ -4624,7 +5238,6 @@ "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, "funding": [ { "type": "opencollective", @@ -4651,6 +5264,7 @@ "resolved": "https://registry.npmjs.org/postcss-preset-mantine/-/postcss-preset-mantine-1.17.0.tgz", "integrity": "sha512-ji1PMDBUf2Vsx/HE5faMSs1+ff6qE6YRulTr4Ja+6HD3gop8rSMTCYdpN7KrdsEg079kfBKkO/PaKhG9uR0zwQ==", "dev": true, + "license": "MIT", "dependencies": { "postcss-mixins": "^9.0.4", "postcss-nested": "^6.0.1" @@ -4663,7 +5277,6 @@ "version": "6.1.2", "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -4678,6 +5291,7 @@ "resolved": "https://registry.npmjs.org/postcss-simple-vars/-/postcss-simple-vars-7.0.1.tgz", "integrity": "sha512-5GLLXaS8qmzHMOjVxqkk1TZPf1jMqesiI7qLhnlyERalG0sMbHIbJqrcnrpmZdKCLglHnRHoEBB61RtGTsj++A==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.0" }, @@ -4693,7 +5307,6 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { @@ -4707,9 +5320,9 @@ } }, "node_modules/prettier": { - "version": "3.4.2", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", - "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", + "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", "dev": true, "license": "MIT", "bin": { @@ -4726,7 +5339,6 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", - "dev": true, "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", @@ -4738,7 +5350,6 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4748,7 +5359,6 @@ "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, "funding": [ { "type": "github", @@ -4790,28 +5400,29 @@ "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", - "dev": true, "license": "MIT" }, "node_modules/react-number-format": { "version": "5.4.3", "resolved": "https://registry.npmjs.org/react-number-format/-/react-number-format-5.4.3.tgz", "integrity": "sha512-VCY5hFg/soBighAoGcdE+GagkJq0230qN6jcS5sp8wQX1qy1fYN/RX7/BXkrs0oyzzwqR8/+eSUrqXbGeywdUQ==", + "license": "MIT", "peerDependencies": { "react": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom": "^0.14 || ^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/react-remove-scroll": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.2.tgz", - "integrity": "sha512-KmONPx5fnlXYJQqC62Q+lwIeAk64ws/cUw6omIumRzMRPqgnYqhSSti99nbj0Ry13bv7dF+BKn7NB+OqkdZGTw==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", + "license": "MIT", "dependencies": { "react-remove-scroll-bar": "^2.3.7", - "react-style-singleton": "^2.2.1", + "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", "use-callback-ref": "^1.3.3", - "use-sidecar": "^1.1.2" + "use-sidecar": "^1.1.3" }, "engines": { "node": ">=10" @@ -4830,6 +5441,7 @@ "version": "2.3.8", "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", + "license": "MIT", "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" @@ -4851,6 +5463,7 @@ "version": "2.2.3", "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", + "license": "MIT", "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" @@ -4872,6 +5485,7 @@ "version": "8.5.6", "resolved": "https://registry.npmjs.org/react-textarea-autosize/-/react-textarea-autosize-8.5.6.tgz", "integrity": "sha512-aT3ioKXMa8f6zHYGebhbdMD2L00tKeRX1zuVuDx9YQK/JLLRSaSxq3ugECEmUB9z2kvk6bFSIoRHLkkUv0RJiw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.20.13", "use-composed-ref": "^1.3.0", @@ -4884,11 +5498,26 @@ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, "license": "MIT", "dependencies": { "pify": "^2.3.0" @@ -4898,7 +5527,6 @@ "version": "3.6.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -4933,7 +5561,8 @@ "node_modules/regenerator-runtime": { "version": "0.14.1", "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz", - "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==" + "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==", + "license": "MIT" }, "node_modules/regexp.prototype.flags": { "version": "1.5.4", @@ -4960,7 +5589,6 @@ "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.0", @@ -5001,18 +5629,22 @@ "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, + "node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, "funding": [ { "type": "github", @@ -5087,6 +5719,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, "node_modules/scheduler": { "version": "0.25.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.25.0.tgz", @@ -5199,7 +5849,6 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" @@ -5212,7 +5861,6 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5298,7 +5946,6 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -5326,6 +5973,15 @@ "node": ">=0.10.0" } }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "license": "MIT", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, "node_modules/stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", @@ -5345,7 +6001,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -5364,7 +6019,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -5379,7 +6033,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5389,14 +6042,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -5522,7 +6173,6 @@ "version": "7.1.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -5539,7 +6189,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -5552,7 +6201,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -5608,7 +6256,6 @@ "version": "3.35.0", "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz", "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, "license": "MIT", "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", @@ -5632,6 +6279,7 @@ "resolved": "https://registry.npmjs.org/sugarss/-/sugarss-4.0.1.tgz", "integrity": "sha512-WCjS5NfuVJjkQzK10s8WOBY+hhDxxNt/N6ZaGwxFZ+wN3/lKKFSaaKUNecULcTTvE4urLcKaZFQD8vO0mOZujw==", "dev": true, + "license": "MIT", "engines": { "node": ">=12.0" }, @@ -5660,7 +6308,6 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" @@ -5669,16 +6316,22 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "license": "MIT" + }, "node_modules/tabbable": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", - "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==", + "license": "MIT" }, "node_modules/tailwindcss": { "version": "3.4.17", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", - "dev": true, "license": "MIT", "dependencies": { "@alloc/quick-lru": "^5.2.0", @@ -5716,7 +6369,6 @@ "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -5733,7 +6385,6 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -5756,7 +6407,6 @@ "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, "license": "MIT", "dependencies": { "any-promise": "^1.0.0" @@ -5766,7 +6416,6 @@ "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, "license": "MIT", "dependencies": { "thenify": ">= 3.1.0 < 4" @@ -5775,11 +6424,34 @@ "node": ">=0.8" } }, + "node_modules/third-party-capital": { + "version": "1.0.20", + "resolved": "https://registry.npmjs.org/third-party-capital/-/third-party-capital-1.0.20.tgz", + "integrity": "sha512-oB7yIimd8SuGptespDAZnNkzIz+NWaJCu2RMsbs4Wmp9zSDUM8Nhi3s2OOcqYuv3mN4hitXc8DVx+LyUmbUDiA==", + "license": "ISC" + }, + "node_modules/tldts": { + "version": "6.1.75", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.75.tgz", + "integrity": "sha512-+lFzEXhpl7JXgWYaXcB6DqTYXbUArvrWAE/5ioq/X3CdWLbDjpPP4XTrQBmEJ91y3xbe4Fkw7Lxv4P3GWeJaNg==", + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.75" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "6.1.75", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.75.tgz", + "integrity": "sha512-AOvV5YYIAFFBfransBzSTyztkc3IMfz5Eq3YluaRiEu55nn43Fzaufx70UqEKYr8BoLCach4q8g/bg6e5+/aFw==", + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" @@ -5788,6 +6460,30 @@ "node": ">=8.0" } }, + "node_modules/tough-cookie": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.0.tgz", + "integrity": "sha512-rvZUv+7MoBYTiDmFPBrhL7Ujx9Sk+q9wwm22x8c8T5IJaR+Wsyc7TNxbVxo84kZoRJZZMazowFLqpankBEQrGg==", + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.0.0.tgz", + "integrity": "sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==", + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/ts-api-utils": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.0.0.tgz", @@ -5805,7 +6501,6 @@ "version": "0.1.13", "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, "license": "Apache-2.0" }, "node_modules/tsconfig-paths": { @@ -5844,6 +6539,7 @@ "version": "4.33.0", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.33.0.tgz", "integrity": "sha512-s6zVrxuyKbbAsSAD5ZPTB77q4YIdRctkTbJ2/Dqlinwz+8ooH2gd+YA7VA6Pa93KML9GockVvoxjZ2vHP+mu8g==", + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=16" }, @@ -5963,9 +6659,9 @@ } }, "node_modules/undici-types": { - "version": "6.19.8", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz", - "integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==", + "version": "6.20.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", + "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", "dev": true, "license": "MIT" }, @@ -5983,6 +6679,7 @@ "version": "1.3.3", "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", + "license": "MIT", "dependencies": { "tslib": "^2.0.0" }, @@ -6003,6 +6700,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/use-composed-ref/-/use-composed-ref-1.4.0.tgz", "integrity": "sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -6016,6 +6714,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz", "integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==", + "license": "MIT", "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, @@ -6029,6 +6728,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/use-latest/-/use-latest-1.3.0.tgz", "integrity": "sha512-mhg3xdm9NaM8q+gLT8KryJPnRFOz1/5XPBhmDEVZK1webPzDjrPk7f/mbpeLqTgB9msytYWANxgALOCJKnLvcQ==", + "license": "MIT", "dependencies": { "use-isomorphic-layout-effect": "^1.1.1" }, @@ -6045,6 +6745,7 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", + "license": "MIT", "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" @@ -6066,14 +6767,67 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, "license": "MIT" }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.1.0.tgz", + "integrity": "sha512-jlf/foYIKywAt3x/XWKZ/3rz8OSJPiWktjmk891alJUEjiVxKX9LEO92qH3hv4aJ0mN3MWPvGMCy8jQi95xK4w==", + "license": "MIT", + "dependencies": { + "tr46": "^5.0.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" @@ -6187,7 +6941,6 @@ "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -6206,7 +6959,6 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -6224,7 +6976,6 @@ "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -6234,14 +6985,12 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, "license": "MIT" }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -6256,7 +7005,6 @@ "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -6269,7 +7017,6 @@ "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", - "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -6278,11 +7025,46 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "license": "MIT" + }, "node_modules/yaml": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, "license": "ISC", "bin": { "yaml": "bin.mjs" diff --git a/frontend/package.json b/frontend/package.json index 7f3cb7a..50c3516 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,24 +10,35 @@ "format": "npx prettier . --write" }, "dependencies": { - "@mantine/core": "^7.16.1", + "@mantine/core": "^7.17.0", "@mantine/hooks": "^7.16.1", - "@tabler/icons-react": "^3.28.1", - "next": "15.1.5", + "@mantine/notifications": "^7.17.1", + "@next/third-parties": "^15.2.0", + "@tabler/icons-react": "^3.30.0", + "@tailwindcss/typography": "^0.5.16", + "@vercel/analytics": "^1.5.0", + "@vercel/speed-insights": "^1.2.0", + "dompurify": "^3.2.3", + "isomorphic-dompurify": "^2.22.0", + "jsdom": "^26.0.0", + "mongodb": "^6.14.2", + "next": "15.1.7", "react": "^19.0.0", "react-dom": "^19.0.0" }, "devDependencies": { "@eslint/eslintrc": "^3", - "@types/node": "^20", + "@next/eslint-plugin-next": "^15.1.7", + "@types/node": "^22", "@types/react": "^19", "@types/react-dom": "^19", "eslint": "^9", - "eslint-config-next": "15.1.5", - "postcss": "^8.5.1", + "eslint-config-next": "15.1.7", + "eslint-plugin-react-hooks": "^5.1.0", + "postcss": "^8.5.3", "postcss-preset-mantine": "^1.17.0", "postcss-simple-vars": "^7.0.1", - "prettier": "3.4.2", + "prettier": "3.5.3", "tailwindcss": "^3.4.1", "typescript": "^5" } diff --git a/frontend/src/app/actions.ts b/frontend/src/app/actions.ts new file mode 100644 index 0000000..59efea9 --- /dev/null +++ b/frontend/src/app/actions.ts @@ -0,0 +1,77 @@ +// frontend/src/app/actions/feedback.ts +"use server"; + +// Type for our form data +export interface FeedbackFormData { + email?: string; + message: string; +} + +export async function submitFeedback(data: FeedbackFormData) { + const databaseId = process.env.NOTION_DATABASE_ID; + const notionApiKey = process.env.NOTION_API_KEY; + + if (!databaseId) { + throw new Error("NOTION_DATABASE_ID environment variable is not set"); + } + + if (!notionApiKey) { + throw new Error("NOTION_API_KEY environment variable is not set"); + } + + try { + const response = await fetch("https://api.notion.com/v1/pages", { + method: "POST", + headers: { + Authorization: `Bearer ${notionApiKey}`, + "Content-Type": "application/json", + "Notion-Version": "2022-06-28", + }, + body: JSON.stringify({ + parent: { + database_id: databaseId, + }, + properties: { + Email: { + title: [ + { + text: { + content: data.email || "Anonymous", + }, + }, + ], + }, + Feedback: { + rich_text: [ + { + text: { + content: data.message, + }, + }, + ], + }, + Date: { + date: { + start: new Date().toISOString(), + }, + }, + }, + }), + }); + + if (!response.ok) { + const errorData = await response.json(); + return { + success: false, + message: `Notion returned an API error: ${errorData.message || response.statusText}`, + }; + } + + return { success: true, message: "Feedback submitted successfully." }; + } catch (error) { + return { + success: false, + message: `Encountered an internal error: ${error}`, + }; + } +} diff --git a/frontend/src/app/favicon.ico b/frontend/src/app/favicon.ico new file mode 100644 index 0000000..d1e84a1 Binary files /dev/null and b/frontend/src/app/favicon.ico differ diff --git a/frontend/src/app/globals.css b/frontend/src/app/globals.css index 2fcb55d..c40b579 100644 --- a/frontend/src/app/globals.css +++ b/frontend/src/app/globals.css @@ -1,4 +1,6 @@ -@tailwind base; +@layer tailwind { + @tailwind base; +} @tailwind components; @tailwind utilities; @layer utilities { @@ -12,3 +14,40 @@ scrollbar-width: none; /* Firefox */ } } + +@layer base { + :root { + --selected: #3a3a3a; + --background: #1f1f1f; + --secondary: #2e2e2e; + --accent: #ffe22f; + } + .dark { + --selected: #3a3a3a; + --background: #1f1f1f; + --secondary: #2e2e2e; + --accent: #ffe22f; + } +} + +.underline-fancy { + text-decoration: underline; + font-weight: bold; + text-decoration-color: var(--mantine-color-accent-0); + text-underline-offset: 4px; + text-decoration-thickness: 2px; +} + +html, +body, +main { + height: 100svh; + margin: 0; + padding: 0; + overflow: hidden; +} + +.prose { + max-width: none; + word-break: break-word; +} diff --git a/frontend/src/app/jobs/[id]/@modal/default.tsx b/frontend/src/app/jobs/[id]/@modal/default.tsx deleted file mode 100644 index 0872c91..0000000 --- a/frontend/src/app/jobs/[id]/@modal/default.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import JobDetails from "@/components/jobs/details/job-details"; - -import { Modal, ScrollArea } from "@mantine/core"; - -export default function JobModal() { - return ( - {}} - size="lg" - title="Job Details" - scrollAreaComponent={ScrollArea} - > - - - ); -} diff --git a/frontend/src/app/jobs/[id]/loading.tsx b/frontend/src/app/jobs/[id]/loading.tsx new file mode 100644 index 0000000..867fa7e --- /dev/null +++ b/frontend/src/app/jobs/[id]/loading.tsx @@ -0,0 +1,12 @@ +// frontend/src/app/jobs/[id]/loading.tsx +import JobDetailsLoading from "@/components/layout/job-details-loading"; + +export default function JobIdLoading() { + return ( +
+
+ +
+
+ ); +} diff --git a/frontend/src/app/jobs/[id]/page.tsx b/frontend/src/app/jobs/[id]/page.tsx index 67a183d..a632245 100644 --- a/frontend/src/app/jobs/[id]/page.tsx +++ b/frontend/src/app/jobs/[id]/page.tsx @@ -1,3 +1,68 @@ -export default function JobDetailPage() { - return
Job Detail
; +// src/app/jobs/[id]/page.tsx + +import { getJobById } from "@/app/jobs/actions"; +import { notFound } from "next/navigation"; +import { Job } from "@/types/job"; +import JobDetailsWrapper from "@/components/jobs/job-details-wrapper"; +import { Metadata } from "next"; +import OgImage from "@/assets/OgImage.png"; + +type Props = { + params: Promise<{ id: string }>; + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}; + +export async function generateMetadata({ params }: Props): Promise { + // Fetch the job + const { id } = await params; + const job: Job | null = await getJobById(id); + + // Fallback to parent metadata if job not found + if (!job) { + return { + title: "Job Not Found", + description: "The requested job could not be found.", + }; + } + + // Create dynamic title and description + const title = `${job.title} at ${job.company.name}`; + const description = + job.one_liner || `${job.title} position at ${job.company.name}`; + + return { + title, + description, + openGraph: { + title, + description, + images: [ + { + url: OgImage.src, + alt: title, + }, + ], + }, + }; +} + +type PageProps = { + params: Promise<{ id: string }>; +}; + +export default async function JobDetailPage({ params }: PageProps) { + const { id } = await params; + const job: Job | null = await getJobById(id); + + if (!job) { + return notFound(); + } + + return ( +
+
+ +
+
+ ); } diff --git a/frontend/src/app/jobs/actions.ts b/frontend/src/app/jobs/actions.ts new file mode 100644 index 0000000..4752c60 --- /dev/null +++ b/frontend/src/app/jobs/actions.ts @@ -0,0 +1,194 @@ +// /src/app/jobs/actions.ts +"use server"; + +import { MongoClient, ObjectId } from "mongodb"; +import { JobFilters } from "@/types/filters"; +import { Job } from "@/types/job"; +import serializeJob from "@/lib/utils"; + +const PAGE_SIZE = 20; + +// Define the MongoJob interface with the correct DB field names. +export interface MongoJob extends Omit { + _id: ObjectId; + is_sponsored: boolean; +} + +/** + * Helper function to build a query object from filters. + * @param filters - The job filters from the client. + * @param additional - Additional query overrides (e.g. { is_sponsor: true }). + * @returns The query object to use with MongoDB. + */ +function buildJobQuery( + filters: Partial, + additional?: Record, +) { + const array_jobs = JSON.parse(JSON.stringify(filters, null, 2)); + const query = { + outdated: false, + ...(array_jobs["workingRights[]"] !== undefined && + array_jobs["workingRights[]"].length && { + working_rights: { + $in: Array.isArray(array_jobs["workingRights[]"]) + ? array_jobs["workingRights[]"] + : [array_jobs["workingRights[]"]], + }, + }), + ...(array_jobs["locations[]"] !== undefined && + array_jobs["locations[]"].length && { + locations: { + $in: Array.isArray(array_jobs["locations[]"]) + ? array_jobs["locations[]"] + : [array_jobs["locations[]"]], + }, + }), + ...(array_jobs["industryFields[]"] !== undefined && + array_jobs["industryFields[]"].length && { + industry_field: { + $in: Array.isArray(array_jobs["industryFields[]"]) + ? array_jobs["industryFields[]"] + : [array_jobs["industryFields[]"]], + }, + }), + ...(array_jobs["jobTypes[]"] !== undefined && + array_jobs["jobTypes[]"].length && { + type: { + $in: Array.isArray(array_jobs["jobTypes[]"]) + ? array_jobs["jobTypes[]"] + : [array_jobs["jobTypes[]"]], + }, + }), + ...(filters.search && { + $or: [ + { title: { $regex: filters.search, $options: "i" } }, + { "company.name": { $regex: filters.search, $options: "i" } }, + ], + }), + ...additional, + }; + return query; +} + +/** + * Helper function to manage a MongoDB connection. + * @param callback - The function that uses the connected MongoClient. + * @returns The result from the callback. + */ +async function withDbConnection( + callback: (client: MongoClient) => Promise, +): Promise { + if (!process.env.MONGODB_URI) { + throw new Error( + "MongoDB URI is not configured. Please check environment variables.", + ); + } + const client = new MongoClient(process.env.MONGODB_URI); + try { + await client.connect(); + return await callback(client); + } finally { + await client.close(); + } +} + +/** + * Fetches paginated and filtered job listings from MongoDB. + */ +export async function getJobs( + filters: Partial, + minSponsors: number = -1, + prioritySponsors: Array = ["IMC", "Atlassian"], +): Promise<{ jobs: Job[]; total: number }> { + return await withDbConnection(async (client) => { + const collection = client.db("default").collection("active_jobs"); + const query = buildJobQuery(filters); + const page = filters.page || 1; + const skip = (page - 1) * PAGE_SIZE; + minSponsors = minSponsors === -1 ? (page == 1 ? 3 : 0) : minSponsors; + + if (minSponsors == 0) { + const [jobs, total] = await Promise.all([ + collection + .find(query) + .sort({ created_at: -1 }) + .skip(skip) + .limit(PAGE_SIZE) + .toArray(), + collection.countDocuments(query), + ]); + return { + // Serialize Job and set highlight to false + jobs: (jobs as MongoJob[]) + .map(serializeJob) + .map((job) => ({ ...job, highlight: false })), + total, + }; + } else { + // Modify query to include sponsored job filtering + const sponsoredQuery = { ...query, is_sponsored: true }; + + // Fetch sponsored jobs (without priority filtering) + let sponsoredJobs = await collection + .aggregate([ + { $match: sponsoredQuery }, + { $sample: { size: minSponsors * 8 } }, + ]) + .toArray(); + + // Apply 65% chance selection for priority sponsors + sponsoredJobs = sponsoredJobs + .filter((job) => { + const isPriority = prioritySponsors.includes(job.company.name); + return isPriority ? Math.random() < 0.65 : Math.random() >= 0.35; // 65% chance for priority, 35% for others + }) + .slice(0, minSponsors) // Ensure we only take the required number + + .map((job) => ({ ...job, highlight: true })); // Add highlight property + + // Get IDs of selected sponsored jobs to exclude them from regular jobs + const sponsoredJobIds = sponsoredJobs.map((job) => job._id); + + // Modify the main query to exclude sponsored jobs we already fetched + const filteredQuery = { ...query, _id: { $nin: sponsoredJobIds } }; + + // Fetch remaining jobs with pagination + const [otherJobs, total] = await Promise.all([ + collection + .find(filteredQuery) + .sort({ created_at: -1 }) + .skip(skip) + .limit(PAGE_SIZE - sponsoredJobs.length) + .toArray(), + collection.countDocuments(query), // Total should still include all jobs matching the original query + ]); + // Merge jobs and make sure we don't exceed PAGE_SIZE also add highlight property + const mergedJobs = [ + ...sponsoredJobs.map((job) => ({ ...job, highlight: true })), + ...otherJobs.map((job) => ({ ...job, highlight: false })), + ].slice(0, PAGE_SIZE); + + return { + jobs: (mergedJobs as MongoJob[]).map(serializeJob), + total, + }; + } + }); +} + +/** + * Fetches a single job by its id. + */ +export async function getJobById(id: string): Promise { + return await withDbConnection(async (client) => { + const collection = client.db("default").collection("active_jobs"); + const job = await collection.findOne({ + _id: new ObjectId(id), + outdated: false, + }); + if (!job) { + return null; + } + return serializeJob(job as MongoJob); + }); +} diff --git a/frontend/src/app/jobs/error.tsx b/frontend/src/app/jobs/error.tsx index 364eeb9..a50ff1b 100644 --- a/frontend/src/app/jobs/error.tsx +++ b/frontend/src/app/jobs/error.tsx @@ -1,4 +1,26 @@ +// frontend/src/app/jobs/error.tsx "use client"; -export default function JobError() { - return
Job Error
; + +import { useEffect } from "react"; +import { Text, Button } from "@mantine/core"; + +export default function JobError({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error(error); + }, [error]); + + return ( +
+ + Failed to render jobs page. Check the console for more details. + + +
+ ); } diff --git a/frontend/src/app/jobs/layout.tsx b/frontend/src/app/jobs/layout.tsx index 7ea3d8f..6a1572c 100644 --- a/frontend/src/app/jobs/layout.tsx +++ b/frontend/src/app/jobs/layout.tsx @@ -1,10 +1,5 @@ -import { JobsProvider } from "@/context/jobs/jobs-provider"; import { PropsWithChildren } from "react"; export default function JobsLayout({ children }: PropsWithChildren) { - return ( - -
{children}
-
- ); + return
{children}
; } diff --git a/frontend/src/app/jobs/loading.tsx b/frontend/src/app/jobs/loading.tsx index 04cf754..a2688d4 100644 --- a/frontend/src/app/jobs/loading.tsx +++ b/frontend/src/app/jobs/loading.tsx @@ -1,3 +1,17 @@ -export default function JobLoading() { - return
Loading Job...
; +// frontend/src/app/jobs/loading.tsx +import MainContentLoading from "@/components/layout/main-content-loading"; + +export default function Loading() { + return ( +
+ {/* FilterSection placeholder - this should always be visible */} +
+
+
+
+
+
+ +
+ ); } diff --git a/frontend/src/app/jobs/page.tsx b/frontend/src/app/jobs/page.tsx index 940edc1..99c5f88 100644 --- a/frontend/src/app/jobs/page.tsx +++ b/frontend/src/app/jobs/page.tsx @@ -1,28 +1,53 @@ -import SearchBar from "@/components/jobs/search/search-bar"; -import FilterSection from "@/components/jobs/filters/filter-section"; -import JobList from "@/components/jobs/details/job-list"; -import JobDetails from "@/components/jobs/details/job-details"; -import { Title } from "@mantine/core"; +// frontend/src/app/jobs/page.tsx +import FilterSection from "@/components/filters/filter-section"; +import JobList from "@/components/jobs/job-list"; +import JobDetails from "@/components/jobs/job-details"; +import { JobFilters } from "@/types/filters"; +import { getJobs } from "@/app/jobs/actions"; +import NoResults from "@/components/ui/no-results"; +import { Suspense } from "react"; +import JobListLoading from "@/components/layout/job-list-loading"; +import JobDetailsLoading from "@/components/layout/job-details-loading"; + +export const metadata = { + title: "Jobs", +}; + +export default async function JobsPage({ + searchParams, +}: { + searchParams: Promise>; +}) { + // https://nextjs.org/docs/app/api-reference/file-conventions/page#searchparams-optional + // searchParams is a promise that resolves to an object containing the search + // parameters of the current URL. + + const filters = await searchParams; + + // Fetch regular jobs with pagination. + const { jobs, total } = await getJobs(filters); -export default function JobsPage() { return ( -
- Find Internships and Student Jobs - - + <> + -
-
- -
+ {total <= 0 ? ( + + ) : ( +
+
+ }> + + +
- {/* Sticky Job Details - hidden on mobile, 70% on desktop */} -
-
- +
+ }> + +
-
-
+ )} + ); } diff --git a/frontend/src/app/layout.tsx b/frontend/src/app/layout.tsx index 6da4d62..81e9e39 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,25 +1,76 @@ -import NavBar from "@/components/layout/nav-bar"; -import "./globals.css"; +// Order seems to matter. If Mantine is imported after tailwind, +// the tailwind class passed with className is not applied. import "@mantine/core/styles.css"; +import "./globals.css"; +import "@mantine/notifications/styles.css"; + +import { Analytics } from "@vercel/analytics/react"; +import { SpeedInsights } from "@vercel/speed-insights/next"; +import { GoogleAnalytics } from "@next/third-parties/google"; + +import NavBar from "@/components/layout/nav-bar"; import { MantineProvider } from "@mantine/core"; import { ColorSchemeScript } from "@mantine/core"; -import { PropsWithChildren } from "react"; +import { PropsWithChildren, Suspense } from "react"; import Head from "next/head"; import { theme } from "@/lib/theme"; +import { Poppins } from "next/font/google"; +import { FilterProvider } from "@/context/filter/filter-provider"; +import { Metadata } from "next"; +import FeedbackButton from "@/components/ui/feedback-button"; +import { Notifications } from "@mantine/notifications"; + +import OgImage from "../assets/OgImage.png"; +import FirstVisitNotification from "@/components/ui/first-visit-notification"; + +export const metadata: Metadata = { + title: { + template: "%s | MAC Jobs Board", + default: "MAC Jobs Board", + }, + openGraph: { + title: "MAC Jobs Board", + description: "Stay ahead with the job board that never sleeps.", + images: [ + { + url: OgImage.src, + alt: "MAC Jobs Board", + }, + ], + }, +}; + +const poppins = Poppins({ + subsets: ["latin"], + weight: ["400", "500", "600", "700"], +}); + export default function RootLayout({ children }: PropsWithChildren) { return ( - + - - -
- -
{children}
-
-
+ + + + +
+ + +
+ {children} + + + + + +
+
+
+
+
); diff --git a/frontend/src/app/loading.tsx b/frontend/src/app/loading.tsx deleted file mode 100644 index fc80ef0..0000000 --- a/frontend/src/app/loading.tsx +++ /dev/null @@ -1,3 +0,0 @@ -export default function Loading() { - return
Loading...
; -} diff --git a/frontend/src/app/page.tsx b/frontend/src/app/page.tsx index d169d82..541f178 100644 --- a/frontend/src/app/page.tsx +++ b/frontend/src/app/page.tsx @@ -1,3 +1,88 @@ +"use client"; + +import { Button } from "@mantine/core"; +import { useEffect } from "react"; +import Link from "next/link"; +import { IconArrowRight } from "@tabler/icons-react"; +import DotBackground from "@/components/ui/dot-background"; +import { useFilterContext } from "@/context/filter/filter-context"; export default function Page() { - return
Home Page
; + const { filters, updateFilters } = useFilterContext(); + + useEffect(() => { + document.title = "Home | MAC Jobs Board"; + }, []); + + const handleGradJobsClick = () => { + updateFilters({ + filters: { + ...filters.filters, + jobTypes: ["GRADUATE"], + page: 1, + }, + }); + }; + + const handleInternJobsClick = () => { + updateFilters({ + filters: { + ...filters.filters, + jobTypes: ["INTERN"], + page: 1, + }, + }); + }; + + return ( + <> + + +
+
+ + Proudly Open Source → + +
+ +

+ Stay ahead with the job board that{" "} + never{" "} + sleeps. +

+ +

+ Stop wasting hours manually tracking job sites. Our smart robots work + 24/7 to find you the most up to date listings so you can focus on what + really matters. +

+ +
+ + + +
+
+ + ); } diff --git a/frontend/src/assets/OgImage.png b/frontend/src/assets/OgImage.png new file mode 100644 index 0000000..fc6cbd8 Binary files /dev/null and b/frontend/src/assets/OgImage.png differ diff --git a/frontend/src/assets/mac.svg b/frontend/src/assets/mac.svg new file mode 100644 index 0000000..a5e9ec5 --- /dev/null +++ b/frontend/src/assets/mac.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/components/filters/dropdown-filter.tsx b/frontend/src/components/filters/dropdown-filter.tsx new file mode 100644 index 0000000..da28946 --- /dev/null +++ b/frontend/src/components/filters/dropdown-filter.tsx @@ -0,0 +1,110 @@ +// frontend/src/components/jobs/filters/dropdown-filter.tsx +import { useEffect, useState } from "react"; +import { + Checkbox, + Combobox, + Group, + Input, + Text, + useCombobox, +} from "@mantine/core"; +import { IconChevronDown } from "@tabler/icons-react"; +import { useFilterContext } from "@/context/filter/filter-context"; +import { JobFilters } from "@/types/filters"; +import { formatCapString, getPluralLabel } from "@/lib/utils"; + +interface DropdownFilterProps { + label: string; + filterKey: keyof JobFilters; + options: string[]; +} + +export default function DropdownFilter({ + label, + filterKey, + options, +}: DropdownFilterProps) { + const combobox = useCombobox({ + onDropdownClose: () => combobox.resetSelectedOption(), + onDropdownOpen: () => combobox.updateSelectedOptionIndex("active"), + }); + + const { filters, updateFilters } = useFilterContext(); + const [localSelected, setLocalSelected] = useState( + (filters.filters[filterKey] as string[]) || [], + ); + + // Sync when filters change + useEffect(() => { + setLocalSelected((filters.filters[filterKey] as string[]) || []); + }, [filters.filters, filterKey]); + + // Updates locally selected value & filters + const handleValueSelect = (value: string) => { + const newValues = localSelected.includes(value) + ? localSelected.filter((item) => item !== value) + : [...localSelected, value]; + + setLocalSelected(newValues); + updateFilters({ + filters: { + ...filters.filters, + [filterKey]: newValues, + page: 1, + }, + }); + }; + + const getDisplayText = () => { + if (localSelected.length === 0) return label; + if (localSelected.length === 1) return formatCapString(localSelected[0]); + return `${localSelected.length} ${getPluralLabel(label)}`; + }; + + return ( + + + } + onClick={() => combobox.toggleDropdown()} + className={`min-w-32`} + > + 0 ? "light" : "dimmed"}> + {getDisplayText()} + + + + + + + {options.map((option) => ( + + + {}} + aria-hidden + tabIndex={-1} + style={{ pointerEvents: "none" }} + /> + {formatCapString(option)} + + + ))} + + + + ); +} diff --git a/frontend/src/components/filters/filter-modal.tsx b/frontend/src/components/filters/filter-modal.tsx new file mode 100644 index 0000000..bb130da --- /dev/null +++ b/frontend/src/components/filters/filter-modal.tsx @@ -0,0 +1,94 @@ +import { Modal, Button, ScrollArea, Group } from "@mantine/core"; +import { IconFilter } from "@tabler/icons-react"; +import { useState } from "react"; +import { FilterSectionGroup } from "./filter-section-group"; +import { useFilterContext } from "@/context/filter/filter-context"; +import { + INDUSTRY_FIELDS, + LOCATIONS, + WORKING_RIGHTS, + JOB_TYPES, +} from "@/types/job"; +import ResetFilters from "@/components/filters/reset-filters"; + +export default function FilterModal() { + const [opened, setOpened] = useState(false); + const { filters, updateFilters } = useFilterContext(); + + const handleToggle = (filterKey: string, value: string) => { + // @ts-expect-error TODO: Fix type error + const currentValues = filters.filters[filterKey] as string[]; + const newValues = currentValues.includes(value) + ? currentValues.filter((v) => v !== value) + : [...currentValues, value]; + + updateFilters({ + filters: { + ...filters.filters, + [filterKey]: newValues, + page: 1, + }, + }); + }; + + return ( + <> + + + setOpened(false)} + size="lg" + title={ + + Filter Jobs + + + } + radius="lg" + padding="md" + > + +
+ handleToggle("industryFields", value)} + /> + + handleToggle("locations", value)} + /> + + handleToggle("workingRights", value)} + /> + + handleToggle("jobTypes", value)} + /> +
+
+
+ + ); +} diff --git a/frontend/src/components/filters/filter-section-group.tsx b/frontend/src/components/filters/filter-section-group.tsx new file mode 100644 index 0000000..036e482 --- /dev/null +++ b/frontend/src/components/filters/filter-section-group.tsx @@ -0,0 +1,39 @@ +// frontend/src/components/filters/filter-section-group.tsx +import { Text } from "@mantine/core"; +import { ToggleTag } from "./toggle-tag"; +import { formatCapString } from "@/lib/utils"; + +interface FilterSectionGroupProps { + title: string; + options: string[]; + selectedValues: string[]; + onToggle: (value: string) => void; +} + +export function FilterSectionGroup({ + title, + options, + selectedValues, + onToggle, +}: FilterSectionGroupProps) { + return ( +
+
+ + {title} + +
+ +
+ {options.map((option) => ( + onToggle(option)} + /> + ))} +
+
+ ); +} diff --git a/frontend/src/components/filters/filter-section.tsx b/frontend/src/components/filters/filter-section.tsx new file mode 100644 index 0000000..89a51fa --- /dev/null +++ b/frontend/src/components/filters/filter-section.tsx @@ -0,0 +1,32 @@ +// frontend/src/components/filters/filter-section.tsx +"use client"; +import { Text } from "@mantine/core"; +import { useFilterContext } from "@/context/filter/filter-context"; +import { useEffect } from "react"; +import FilterModal from "@/components/filters/filter-modal"; +import ResetFilters from "@/components/filters/reset-filters"; + +interface FilterSectionProps { + _totalJobs: number; +} + +export default function FilterSection({ _totalJobs }: FilterSectionProps) { + const { totalJobs, setTotalJobs, isLoading } = useFilterContext(); + + useEffect(() => { + setTotalJobs(_totalJobs); + }, [_totalJobs, setTotalJobs]); + + return ( +
+ + {isLoading ? "" : totalJobs + " Results"} + + +
+ + +
+
+ ); +} diff --git a/frontend/src/components/filters/reset-filters.tsx b/frontend/src/components/filters/reset-filters.tsx new file mode 100644 index 0000000..15d4cb5 --- /dev/null +++ b/frontend/src/components/filters/reset-filters.tsx @@ -0,0 +1,45 @@ +// frontend/src/components/ui/reset-filters.tsx +import { Button } from "@mantine/core"; +import { useFilterContext } from "@/context/filter/filter-context"; +import { IconX } from "@tabler/icons-react"; + +interface ResetFiltersProps { + className?: string; + variant?: string; +} + +export default function ResetFilters({ + className = "", + variant = "light", +}: ResetFiltersProps) { + const { filters, clearFilters } = useFilterContext(); + + // Check if any filters are applied + const hasActiveFilters = () => { + const { search, industryFields, jobTypes, locations, workingRights } = + filters.filters; + return ( + search !== "" || + industryFields.length > 0 || + jobTypes.length > 0 || + locations.length > 0 || + workingRights.length > 0 + ); + }; + + if (!hasActiveFilters()) { + return null; + } + + return ( + + ); +} diff --git a/frontend/src/components/filters/toggle-tag.tsx b/frontend/src/components/filters/toggle-tag.tsx new file mode 100644 index 0000000..dd1c6b6 --- /dev/null +++ b/frontend/src/components/filters/toggle-tag.tsx @@ -0,0 +1,24 @@ +// frontend/src/components/filters/toggle-tag.tsx +import { Button } from "@mantine/core"; + +interface ToggleTagProps { + label: string; + isSelected: boolean; + onClick: () => void; +} + +export function ToggleTag({ label, isSelected, onClick }: ToggleTagProps) { + return ( + + ); +} diff --git a/frontend/src/components/jobs/company-logo.tsx b/frontend/src/components/jobs/company-logo.tsx new file mode 100644 index 0000000..790c05b --- /dev/null +++ b/frontend/src/components/jobs/company-logo.tsx @@ -0,0 +1,95 @@ +// frontend/src/components/jobs/company-logo.tsx +import { Image } from "@mantine/core"; +import { useState } from "react"; + +interface CompanyLogoProps { + name: string; + logo?: string; + applicationUrl?: string; + className?: string; +} + +export default function CompanyLogo({ + name, + logo, + applicationUrl, + className = "", +}: CompanyLogoProps) { + const baseClasses = "object-contain rounded-md bg-white"; + const [logoFailed, setLogoFailed] = useState(false); + const [clearbitFailed, setClearbitFailed] = useState(false); + + // Function to extract base domain from URL + const extractBaseDomain = (url: string): string => { + try { + const urlObj = new URL(url); + return urlObj.hostname; + } catch { + return ""; + } + }; + + // Check if URL should be skipped for logo generation + const shouldSkipUrl = (url: string): boolean => { + const skipDomains = [ + "bit.ly", + "tinyurl.com", + "goo.gl", + "gradconnection.com", + "gradconnect.com", + "prosple.com", + "linkedin.com", + "lnkd.in", + "surveymonkey.com", + "forms.gle", + ]; + + try { + const domain = new URL(url).hostname; + return skipDomains.some((skipDomain) => domain.includes(skipDomain)); + } catch { + return true; + } + }; + + // Render the fallback with first letter or question mark + const renderFallback = () => { + return ( +
+
{name ? name.charAt(0).toUpperCase() : "?"}
+
+ ); + }; + + // Case 1: Try company logo first + if (logo && !logoFailed) { + return ( + {name setLogoFailed(true)} + /> + ); + } + + // Case 2: Try Clearbit logo from application URL + if (applicationUrl && !shouldSkipUrl(applicationUrl) && !clearbitFailed) { + const domain = extractBaseDomain(applicationUrl); + if (domain) { + return ( + {name setClearbitFailed(true)} + /> + ); + } + } + + // Case 3 & 4: Fallback to initial letter or question mark + return renderFallback(); +} diff --git a/frontend/src/components/jobs/details/job-card.tsx b/frontend/src/components/jobs/details/job-card.tsx deleted file mode 100644 index 7c1c87e..0000000 --- a/frontend/src/components/jobs/details/job-card.tsx +++ /dev/null @@ -1,14 +0,0 @@ -import { Text } from "@mantine/core"; - -export default function JobCard() { - return ( -
- Job Title - Location - - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean - tincidunt urna ac luctus pellentesque.{" "} - -
- ); -} diff --git a/frontend/src/components/jobs/details/job-details.tsx b/frontend/src/components/jobs/details/job-details.tsx deleted file mode 100644 index 8fe9c2c..0000000 --- a/frontend/src/components/jobs/details/job-details.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import { Text } from "@mantine/core"; - -export default function JobDetails() { - return ( -
- Details here -
- ); -} diff --git a/frontend/src/components/jobs/details/job-list.tsx b/frontend/src/components/jobs/details/job-list.tsx deleted file mode 100644 index c605218..0000000 --- a/frontend/src/components/jobs/details/job-list.tsx +++ /dev/null @@ -1,13 +0,0 @@ -import JobCard from "@/components/jobs/details/job-card"; - -export default function JobList() { - return ( -
- {Array(20) - .fill(0) - .map((_, i) => ( - - ))} -
- ); -} diff --git a/frontend/src/components/jobs/filters/dropdown-filter.tsx b/frontend/src/components/jobs/filters/dropdown-filter.tsx deleted file mode 100644 index 3e8758e..0000000 --- a/frontend/src/components/jobs/filters/dropdown-filter.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import { Select } from "@mantine/core"; - -interface DropdownFilterProps { - label: string; -} - -export default function DropdownFilter({ label }: DropdownFilterProps) { - return ( - - ); -} diff --git a/frontend/src/components/jobs/filters/filter-section.tsx b/frontend/src/components/jobs/filters/filter-section.tsx deleted file mode 100644 index ce1cff9..0000000 --- a/frontend/src/components/jobs/filters/filter-section.tsx +++ /dev/null @@ -1,17 +0,0 @@ -import DropdownFilter from "@/components/jobs/filters/dropdown-filter"; -import DropdownSort from "@/components/jobs/filters/dropdown-sort"; -import { Text } from "@mantine/core"; - -export default function FilterSection() { - return ( -
- 196 Results -
- - - -
- -
- ); -} diff --git a/frontend/src/components/jobs/info-tag.tsx b/frontend/src/components/jobs/info-tag.tsx new file mode 100644 index 0000000..f891c32 --- /dev/null +++ b/frontend/src/components/jobs/info-tag.tsx @@ -0,0 +1,18 @@ +import { Text } from "@mantine/core"; + +interface InfoTagProps { + icon: React.ReactNode; + text: string; + className?: string; +} + +export const InfoTag = ({ icon, text, className }: InfoTagProps) => ( +
+ {icon} + + {text} + +
+); diff --git a/frontend/src/components/jobs/job-card.tsx b/frontend/src/components/jobs/job-card.tsx new file mode 100644 index 0000000..5490f90 --- /dev/null +++ b/frontend/src/components/jobs/job-card.tsx @@ -0,0 +1,90 @@ +// frontend/src/components/jobs/details/job-card.tsx +import { Box } from "@mantine/core"; +import { Job } from "@/types/job"; +import { formatCapString, getTimeAgo } from "@/lib/utils"; +import Badge from "@/components/ui/badge"; +import DOMPurify from "isomorphic-dompurify"; +import CompanyLogo from "@/components/jobs/company-logo"; + +interface JobCardProps { + job: Job; + isSelected?: boolean; + isSponsor?: boolean; +} +const removeImageTags = (content: string): string => { + return content.replace(/]*>/g, ""); +}; + +export default function JobCard({ job, isSelected, isSponsor }: JobCardProps) { + const washedDescription = job.one_liner ? removeImageTags(job.one_liner) : ""; + return ( + + {/* Top section - company info */} +
+
+
+ +
+ + {job.title} + + + {job.company.name} + +
+
+ + {getTimeAgo(job.created_at)} + +
+
+
+ + {/* Bottom section - badges */} +
+ {/* Show a yellow "Sponsored" badge if this is a sponsor card */} + {isSponsor && } + {job.type && } + {job.working_rights?.[0] && ( + + )} + {!isSponsor && job.industry_field && ( + + )} + {job.locations && job.locations.length > 0 && ( + formatCapString(loc)) + .join(", ")}${job.locations.length > 2 ? ", ..." : ""}`} + /> + )} +
+ + ); +} diff --git a/frontend/src/components/jobs/job-description.tsx b/frontend/src/components/jobs/job-description.tsx new file mode 100644 index 0000000..b30c38d --- /dev/null +++ b/frontend/src/components/jobs/job-description.tsx @@ -0,0 +1,28 @@ +// frontend/src/components/jobs/details/sections/job-description.tsx +import SectionHeading from "@/components/ui/section-heading"; +import { TypographyStylesProvider } from "@mantine/core"; +import DOMPurify from "isomorphic-dompurify"; +import { IconBook } from "@tabler/icons-react"; + +interface JobDescriptionProps { + description: string; +} + +export default function JobDescription({ description }: JobDescriptionProps) { + return ( +
+ } + /> + +
+ +
+ ); +} diff --git a/frontend/src/components/jobs/job-details-wrapper.tsx b/frontend/src/components/jobs/job-details-wrapper.tsx new file mode 100644 index 0000000..726da0e --- /dev/null +++ b/frontend/src/components/jobs/job-details-wrapper.tsx @@ -0,0 +1,22 @@ +// src/components/JobDetailsWrapper.tsx +"use client"; + +import { useEffect } from "react"; +import { useFilterContext } from "@/context/filter/filter-context"; +import JobDetails from "@/components/jobs/job-details"; +import { Job } from "@/types/job"; + +interface JobDetailsWrapperProps { + job: Job; +} + +export default function JobDetailsWrapper({ job }: JobDetailsWrapperProps) { + const { setSelectedJob } = useFilterContext(); + + useEffect(() => { + // Set the fetched job into context + setSelectedJob(job); + }, [job, setSelectedJob]); + + return ; +} diff --git a/frontend/src/components/jobs/job-details.tsx b/frontend/src/components/jobs/job-details.tsx new file mode 100644 index 0000000..55aaa78 --- /dev/null +++ b/frontend/src/components/jobs/job-details.tsx @@ -0,0 +1,109 @@ +// frontend/src/components/jobs/job-details.tsx +"use client"; +import { useEffect, useRef, useState } from "react"; +import { ActionIcon, Button, Card, ScrollArea } from "@mantine/core"; +import { IconCheck, IconCopy, IconExternalLink } from "@tabler/icons-react"; +import { useFilterContext } from "@/context/filter/filter-context"; +import JobDescription from "@/components/jobs/job-description"; +import JobHeader from "@/components/jobs/job-header"; +import JobDetailsLoading from "@/components/layout/job-details-loading"; +import JobSummary from "@/components/jobs/job-summary"; + +export default function JobDetails() { + const { selectedJob, isLoading } = useFilterContext(); + const scrollRef = useRef(null); + const [isCopied, setIsCopied] = useState(false); + const timeoutRef = useRef(null); + + // Scroll to top whenever a new job is selected + useEffect(() => { + if (scrollRef.current) { + scrollRef.current.scrollTo({ top: 0 }); + } + }, [selectedJob]); + + useEffect(() => { + return () => { + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + }; + }, []); + + if (!selectedJob || isLoading) { + return ; + } + + const handleApplyClick = () => { + window.open(selectedJob.application_url, "_blank"); + }; + + const handleCopyLink = () => { + const jobUrl = `${window.location.origin}/jobs/${selectedJob.id}`; + if (navigator && navigator.clipboard) { + navigator.clipboard.writeText(jobUrl); + } + + setIsCopied(true); + + // Reset copied state after 2 seconds + if (timeoutRef.current) { + clearTimeout(timeoutRef.current); + } + + timeoutRef.current = setTimeout(() => { + setIsCopied(false); + }, 500); + }; + + return ( + + + + {selectedJob && selectedJob.one_liner && ( + + )} + {selectedJob && selectedJob.description && ( + + )} + + +
+ + + {isCopied ? : } + + +
+
+ ); +} diff --git a/frontend/src/components/jobs/job-header.tsx b/frontend/src/components/jobs/job-header.tsx new file mode 100644 index 0000000..1ca1449 --- /dev/null +++ b/frontend/src/components/jobs/job-header.tsx @@ -0,0 +1,88 @@ +// frontend/src/components/jobs/job-header.tsx +import { + IconBriefcase, + IconBuilding, + IconCalendar, + IconId, + IconMapPin, +} from "@tabler/icons-react"; +import { Job } from "@/types/job"; +import { formatCapString, formatWorkingRights, getTimeAgo } from "@/lib/utils"; +import Link from "next/link"; +import CompanyLogo from "@/components/jobs/company-logo"; +import { InfoTag } from "@/components/jobs/info-tag"; + +interface JobHeaderProps { + job: Job; +} + +export default function JobHeader({ job }: JobHeaderProps) { + return ( +
+
+ {/* Title with responsive sizing and padding */} + + {job.title} + + + {/* Company name link */} + + {job.company.name} + + + {/* Info tags flexbox container */} +
+ {/* Location */} + } + text={job.locations + ?.map((location) => formatCapString(location)) + .join(", ")} + /> + + {/* Date found */} + } + text={`Found ${getTimeAgo(job.created_at)}`} + /> + + {/* Role type */} + {job.type && ( + } + text={`${formatCapString(job.type)} Role`} + /> + )} + + {/* Industry field */} + {job.industry_field && ( + } + text={formatCapString(job.industry_field)} + /> + )} + + {/* Working Rights */} + {job.working_rights && ( + } + text={formatWorkingRights(job.working_rights)} + /> + )} +
+
+ + {/* Company logo with responsive sizing */} + +
+ ); +} diff --git a/frontend/src/components/jobs/job-list.tsx b/frontend/src/components/jobs/job-list.tsx new file mode 100644 index 0000000..3871b56 --- /dev/null +++ b/frontend/src/components/jobs/job-list.tsx @@ -0,0 +1,89 @@ +// frontend/src/components/jobs/job-list.tsx +"use client"; + +import JobCard from "@/components/jobs/job-card"; +import { useFilterContext } from "@/context/filter/filter-context"; +import { Job } from "@/types/job"; +import { useEffect, useState } from "react"; +import { Modal, ScrollArea } from "@mantine/core"; +import JobDetails from "@/components/jobs/job-details"; +import JobListLoading from "@/components/layout/job-list-loading"; +import JobPagination from "@/components/jobs/job-pagination"; +import { useMediaQuery } from "@mantine/hooks"; + +interface JobListProps { + jobs: Job[]; // Regular jobs +} + +export default function JobList({ jobs }: JobListProps) { + //export default function JobList({ jobs, sponsoredJobs }: JobListProps) { + const { selectedJob, setSelectedJob, isLoading } = useFilterContext(); + const [isModalOpen, setIsModalOpen] = useState(false); + const isDesktop = useMediaQuery("(min-width: 1024px)"); + + useEffect(() => { + if (!selectedJob) { + setSelectedJob(jobs[0]); + } + }, [jobs, selectedJob, setSelectedJob]); + + if (isLoading) return ; + + return ( + <> + {/* This is a workaround to ensure mobile job cards are unaffected by the scrollbar. */} + +
+ {jobs.map((job) => ( +
{ + setSelectedJob(job); + // Only open modal on mobile + if (window.innerWidth < 1024) { + setIsModalOpen(true); + } + }} + className="cursor-pointer" + > + +
+ ))} +
+ +
+ + setIsModalOpen(false)} + size="lg" + scrollAreaComponent={ScrollArea} + className="lg:hidden" + fullScreen + styles={{ + body: { + height: "calc(100svh - 100px)", + }, + }} + > + + + + ); +} diff --git a/frontend/src/components/jobs/job-pagination.tsx b/frontend/src/components/jobs/job-pagination.tsx new file mode 100644 index 0000000..8950489 --- /dev/null +++ b/frontend/src/components/jobs/job-pagination.tsx @@ -0,0 +1,62 @@ +// frontend/src/components/jobs/pagination.tsx +"use client"; + +import { useEffect, useState } from "react"; +import { Pagination } from "@mantine/core"; +import { useFilterContext } from "@/context/filter/filter-context"; + +interface JobPaginationProps { + pageSize?: number; +} + +export default function JobPagination({ pageSize = 20 }: JobPaginationProps) { + const [isReady, setIsReady] = useState(false); + const { filters, updateFilters, totalJobs, isLoading } = useFilterContext(); + + useEffect(() => { + if (totalJobs !== undefined) { + setIsReady(true); + } + }, [totalJobs]); + + const totalPages = Math.ceil(totalJobs / pageSize); + + if (!isReady || totalPages <= 1 || isLoading) return null; + + const handlePageChange = (page: number) => { + const scrollContainer = document.querySelector("#job-list-container"); + if (scrollContainer) { + scrollContainer.scrollTop = 0; + } + + updateFilters({ + filters: { + ...filters.filters, + page, + }, + }); + }; + + return ( + // mb-12 gives extra space for feedback button on mobile. it would've blocked the pagination controls. +
+ ({ + disabled: page === filters.filters.page, + "aria-current": page === filters.filters.page ? "page" : undefined, + className: page === filters.filters.page ? "!opacity-100" : "", + })} + /> +
+ ); +} diff --git a/frontend/src/components/jobs/job-summary.tsx b/frontend/src/components/jobs/job-summary.tsx new file mode 100644 index 0000000..6a51318 --- /dev/null +++ b/frontend/src/components/jobs/job-summary.tsx @@ -0,0 +1,21 @@ +// frontend/src/components/jobs/details/sections/job-description.tsx +import SectionHeading from "@/components/ui/section-heading"; +import { IconRobot } from "@tabler/icons-react"; + +interface JobSummaryProps { + one_liner?: string; +} + +export default function JobSummary({ one_liner }: JobSummaryProps) { + return ( +
+ } + title="Summary" + /> + + {one_liner} + +
+ ); +} diff --git a/frontend/src/components/jobs/search/search-bar.tsx b/frontend/src/components/jobs/search/search-bar.tsx deleted file mode 100644 index 0f33d95..0000000 --- a/frontend/src/components/jobs/search/search-bar.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import { Input } from "@mantine/core"; -import { IconSearch } from "@tabler/icons-react"; - -export default function SearchBar() { - return ( - } - /> - ); -} diff --git a/frontend/src/components/layout/heading-text.tsx b/frontend/src/components/layout/heading-text.tsx new file mode 100644 index 0000000..2369850 --- /dev/null +++ b/frontend/src/components/layout/heading-text.tsx @@ -0,0 +1,8 @@ +export default function HeadingText() { + return ( + + Find Internships and{" "} + Student Jobs + + ); +} diff --git a/frontend/src/components/layout/job-details-loading.tsx b/frontend/src/components/layout/job-details-loading.tsx new file mode 100644 index 0000000..222a680 --- /dev/null +++ b/frontend/src/components/layout/job-details-loading.tsx @@ -0,0 +1,44 @@ +// frontend/src/components/layout/job-details-loading.tsx +import { Card, ScrollArea } from "@mantine/core"; + +export default function JobDetailsLoading() { + return ( + + +
+ {/* Header skeleton */} +
+
+
+
+
+
+
+ + {/* Description section skeleton */} +
+
+
+
+
+
+ {[...Array(8)].map((_, i) => ( +
+ ))} +
+
+
+ + + {/* Action buttons skeleton */} +
+
+
+
+
+ + ); +} diff --git a/frontend/src/components/layout/job-list-loading.tsx b/frontend/src/components/layout/job-list-loading.tsx new file mode 100644 index 0000000..310abf2 --- /dev/null +++ b/frontend/src/components/layout/job-list-loading.tsx @@ -0,0 +1,20 @@ +import { ScrollArea } from "@mantine/core"; + +export default function JobListLoading() { + return ( + +
+ {[...Array(10)].map((_, i) => ( +
+ ))} +
+ + ); +} diff --git a/frontend/src/components/layout/logo.tsx b/frontend/src/components/layout/logo.tsx index 87cd09f..ae8fb97 100644 --- a/frontend/src/components/layout/logo.tsx +++ b/frontend/src/components/layout/logo.tsx @@ -1,5 +1,12 @@ -import { Text } from "@mantine/core"; +import MacLogo from "@/assets/mac.svg"; +import Image from "next/image"; +import Link from "next/link"; export default function Logo() { - return JOB BOARD; + return ( + + MAC Logo + Jobs + + ); } diff --git a/frontend/src/components/layout/main-content-loading.tsx b/frontend/src/components/layout/main-content-loading.tsx new file mode 100644 index 0000000..37e1ec8 --- /dev/null +++ b/frontend/src/components/layout/main-content-loading.tsx @@ -0,0 +1,19 @@ +// frontend/src/app/jobs/loading.tsx +import JobListLoading from "@/components/layout/job-list-loading"; +import JobDetailsLoading from "@/components/layout/job-details-loading"; + +export default function MainContentLoading() { + { + /* Main content area */ + } + return ( +
+
+ +
+
+ +
+
+ ); +} diff --git a/frontend/src/components/layout/nav-bar-mobile.tsx b/frontend/src/components/layout/nav-bar-mobile.tsx new file mode 100644 index 0000000..ae0fcbc --- /dev/null +++ b/frontend/src/components/layout/nav-bar-mobile.tsx @@ -0,0 +1,69 @@ +"use client"; +import Link from "next/link"; +import { Button, Menu } from "@mantine/core"; +import { IconMenu2, IconSearch } from "@tabler/icons-react"; +import Logo from "@/components/layout/logo"; +import SearchBar from "@/components/search/search-bar"; +import { useState } from "react"; +import { usePathname } from "next/navigation"; + +export const NavBarMobile = () => { + const [showSearch, setShowSearch] = useState(false); + const pathname = usePathname(); + + const menuItems = [ + { href: "/", label: "Home" }, + { href: "/jobs", label: "Jobs" }, + ]; + + return ( + <> + {showSearch ? ( +
+ + +
+ ) : ( + <> +
+ +
+
+ + + + + + + {menuItems.map((item) => ( + + {item.label} + + ))} + + +
+ + )} + + ); +}; diff --git a/frontend/src/components/layout/nav-bar.tsx b/frontend/src/components/layout/nav-bar.tsx index e586129..79bfdcd 100644 --- a/frontend/src/components/layout/nav-bar.tsx +++ b/frontend/src/components/layout/nav-bar.tsx @@ -1,19 +1,24 @@ import Logo from "@/components/layout/logo"; -import { Button } from "@mantine/core"; -import Link from "next/link"; +import SearchBar from "@/components/search/search-bar"; +import { NavBarMobile } from "./nav-bar-mobile"; +import NavLinks from "./nav-links"; export default function NavBar() { return ( -