diff --git a/.github/workflows/azure-acr-deploy-frontend-dev.yml b/.github/workflows/azure-acr-deploy-frontend-dev.yml new file mode 100644 index 0000000..e65d175 --- /dev/null +++ b/.github/workflows/azure-acr-deploy-frontend-dev.yml @@ -0,0 +1,71 @@ +name: Frontend - Build and Deploy on Azure (Dev) + +on: + push: + branches: [development] + paths: + - "frontend/**" + - ".github/workflows/azure-deploy-frontend-dev.yml" + workflow_dispatch: + +env: + REGISTRY: mploycontainerregistry-hncsekeah2gagbgb.azurecr.io + FRONTEND_IMAGE_NAME: nextjs-frontend + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:latest + network=host + + - name: Log in to Azure Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} + + - name: Build and push image (with ACR cache) + uses: docker/build-push-action@v6 + with: + context: ./frontend + file: ./frontend/Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:development + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:buildcache,mode=max + + deploy: + needs: build-and-push + runs-on: ubuntu-latest + environment: development + steps: + - name: Azure login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Deploy to Azure Container Apps (Dev) + uses: azure/container-apps-deploy-action@v2 + with: + registryUrl: ${{ env.REGISTRY }} + registryUsername: ${{ secrets.ACR_USERNAME }} + registryPassword: ${{ secrets.ACR_PASSWORD }} + containerAppName: mploy-frontend-dev + resourceGroup: ${{ secrets.AZURE_RESOURCE_GROUP_DEV }} + imageToDeploy: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:development + targetPort: 3000 + environmentVariables: > + NOTION_API_KEY=${{ secrets.NOTION_API_KEY }} + NOTION_DATABASE_ID=${{ secrets.NOTION_DATABASE_ID }} + MONGODB_URI=${{ secrets.MONGODB_URI }} + NODE_ENV=development diff --git a/.github/workflows/azure-acr-deploy-frontend-prod.yml b/.github/workflows/azure-acr-deploy-frontend-prod.yml new file mode 100644 index 0000000..13cd5f5 --- /dev/null +++ b/.github/workflows/azure-acr-deploy-frontend-prod.yml @@ -0,0 +1,78 @@ +name: Frontend - Build and Deploy on Azure (Prod) + +on: + push: + branches: [production] + paths: + - "frontend/**" + - ".github/workflows/azure-deploy-frontend-prod.yml" + workflow_dispatch: + +env: + REGISTRY: mploycontainerregistry-hncsekeah2gagbgb.azurecr.io + FRONTEND_IMAGE_NAME: nextjs-frontend + +jobs: + build-and-push: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + with: + driver-opts: | + image=moby/buildkit:latest + network=host + + - name: Log in to Azure Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ env.REGISTRY }} + username: ${{ secrets.ACR_USERNAME }} + password: ${{ secrets.ACR_PASSWORD }} + + - name: Set version tag + id: vars + run: | + VERSION="v$(date +'%Y.%m.%d')-${GITHUB_RUN_NUMBER}" + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Build and push :production and versioned tags (with ACR cache) + uses: docker/build-push-action@v6 + with: + context: ./frontend + file: ./frontend/Dockerfile + push: true + tags: | + ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:production + ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:${{ steps.vars.outputs.version }} + cache-from: type=registry,ref=${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:buildcache + cache-to: type=registry,ref=${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:buildcache,mode=max + + deploy: + needs: build-and-push + runs-on: ubuntu-latest + environment: production + steps: + - name: Azure login + uses: azure/login@v2 + with: + creds: ${{ secrets.AZURE_CREDENTIALS }} + + - name: Deploy to Azure Container Apps (Prod) + uses: azure/container-apps-deploy-action@v2 + with: + registryUrl: ${{ env.REGISTRY }} + registryUsername: ${{ secrets.ACR_USERNAME }} + registryPassword: ${{ secrets.ACR_PASSWORD }} + containerAppName: mploy-frontend + resourceGroup: ${{ secrets.AZURE_RESOURCE_GROUP_PROD }} + imageToDeploy: ${{ env.REGISTRY }}/${{ env.FRONTEND_IMAGE_NAME }}:production + targetPort: 3000 + environmentVariables: > + NOTION_API_KEY=${{ secrets.NOTION_API_KEY }} + NOTION_DATABASE_ID=${{ secrets.NOTION_DATABASE_ID }} + MONGODB_URI=${{ secrets.MONGODB_URI }} + NODE_ENV=production diff --git a/.github/workflows/azure-static-web-apps-lively-desert-03e284d00.yml b/.github/workflows/azure-static-web-apps-lively-desert-03e284d00.yml deleted file mode 100644 index dd310a4..0000000 --- a/.github/workflows/azure-static-web-apps-lively-desert-03e284d00.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: Azure Static Web Apps CI/CD - -on: - push: - branches: - - production - pull_request: - types: [opened, synchronize, reopened, closed] - branches: - - production - -jobs: - build_and_deploy_job: - if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - name: Build and Deploy Job - steps: - - uses: actions/checkout@v3 - with: - submodules: true - lfs: false - - name: Build And Deploy - id: builddeploy - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LIVELY_DESERT_03E284D00 }} - repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments) - action: "upload" - ###### Repository/Build Configurations - These values can be configured to match your app requirements. ###### - # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig - app_location: "./frontend" # App source code path - api_location: "" # Api source code path - optional - output_location: "" # Built app content directory - optional - ###### End of Repository/Build Configurations ###### - - close_pull_request_job: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - name: Close Pull Request Job - steps: - - name: Close Pull Request - id: closepullrequest - uses: Azure/static-web-apps-deploy@v1 - with: - azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LIVELY_DESERT_03E284D00 }} - action: "close" 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/Makefile b/Makefile deleted file mode 100644 index bf71c6a..0000000 --- a/Makefile +++ /dev/null @@ -1,8 +0,0 @@ -.PHONY: dev dev-docker - -dev: - docker compose -f docker-compose.dev.yml up - -dev-clean: - docker compose -f docker-compose.dev.yml down - docker compose -f docker-compose.dev.yml up --build \ No newline at end of file diff --git a/README.md b/README.md index c5fc1f0..cc445ef 100644 --- a/README.md +++ b/README.md @@ -1,22 +1,66 @@ -# MPloy Job Board +# MPLOY - 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 International students) +- Works perfectly on phone or laptop +- Server-side rendering (where possible) with Next.js 15 App Router +- Multiple filters can be applied at once, including text search +- Desktop/mobile responsive UI: list/details on desktop, modal on mobile +- State persists in URL: search, filters, pagination (`/jobs?q=dev&location=sydney&page=2`) +- Direct job links supported (`/jobs/[id]`) +- Parallel data fetching for faster loads +- Real-time job search with debouncing + +## 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 + +### Key Patterns + +- **State Management**: Start with props; use custom hooks for reusable logic; Context for global state (e.g., job filters). +- **Data Flow**: URL as source of truth for filters/search; debounced API calls; prefetching for pagination. +- **Components**: Keep thin (50-150 lines); use layouts for shared UI; mark client components with "use client". +- **Features Used**: Intercepting routes for modals; Suspense for loading; Error boundaries; useMemo/useRef for optimization. + +### Structure + +``` +src/ +β”œβ”€β”€ app/ +β”‚ β”œβ”€β”€ jobs/ # Main jobs route with filters, listing, details +β”‚ β”‚ β”œβ”€β”€ [id]/ # Dynamic job page +β”‚ β”‚ └── error.tsx # Job-specific error handling +β”‚ β”œβ”€β”€ layout.tsx # Root layout with providers +β”‚ └── page.tsx # Home (redirects to /jobs) +β”œβ”€β”€ components/ +β”‚ β”œβ”€β”€ jobs/ # Job cards, lists, details, filters +β”‚ └── layout/ # Nav, logo, search bar +β”œβ”€β”€ context/ +β”‚ └── filter/ # Filter state provider +└── lib/ # Utils, theme +``` + +## 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 ### Prerequisites + - Node.js 20+ - Java 17 - Go 1.21+ @@ -24,20 +68,21 @@ A modern job board application built with Next.js, Spring Boot, and Go, designed - Redis ### Local Development -```bash -# Start all services -docker compose -f docker-compose.dev.yml up -# Alternative if Make is installed -make dev -# Frontend only +```bash cd frontend npm install npm run dev +``` + +## Environment Variables -# Backend only -cd backend -./gradlew bootRun +``` +MONGODB_URI= +MONGODB_DATABASE=default +MONGODB_COLLECTION=listings + +NODE_ENV=development ``` ## Development Guidelines @@ -45,7 +90,8 @@ cd backend ### Git Workflow #### Branch Structure -- `main` - Production branch + +- `production` - Production branch - `dev` - Development branch - Feature branches follow the pattern: ``` @@ -53,8 +99,20 @@ cd backend Examples: - backend/edwn/redis-caching - frontend/sarah/job-filters - - fullstack/alex/docker-setup ``` - + +### Coding Conventions + +- Use kebab-case for files (e.g., `product-card.ts`); camelCase for hooks (e.g., `useCustomHook`). +- Group related components in feature directories (e.g., `components/jobs/filters/`). +- Prioritize server components; use "use client" only when needed. +- Build with scalability and observability in mind (e.g., structured logging with Pino). + +## Deployment (Development and Production) + +- We are deployed on Azure Container Apps via Github Actions. +- GitHub Actions -> builds containers -> Azure Containers Registry -> Azure Container Apps. + ## License -This project is licensed under the MIT License. \ No newline at end of file + +This project is licensed under the MIT License. diff --git a/backend/.gitattributes b/backend/.gitattributes deleted file mode 100644 index 8af972c..0000000 --- a/backend/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -/gradlew text eol=lf -*.bat text eol=crlf -*.jar binary diff --git a/backend/.gitignore b/backend/.gitignore deleted file mode 100644 index 1daab95..0000000 --- a/backend/.gitignore +++ /dev/null @@ -1,42 +0,0 @@ -HELP.md -.gradle -build/ -!gradle/wrapper/gradle-wrapper.jar -!**/src/main/**/build/ -!**/src/test/**/build/ - -### STS ### -.apt_generated -.classpath -.factorypath -.project -.settings -.springBeans -.sts4-cache -bin/ -!**/src/main/**/bin/ -!**/src/test/**/bin/ - -### IntelliJ IDEA ### -.idea -*.iws -*.iml -*.ipr -out/ -!**/src/main/**/out/ -!**/src/test/**/out/ - -### NetBeans ### -/nbproject/private/ -/nbbuild/ -/dist/ -/nbdist/ -/.nb-gradle/ - -### VS Code ### -.vscode/ - -### Kotlin ### -.kotlin - -.idea \ No newline at end of file diff --git a/backend/Dockerfile.dev b/backend/Dockerfile.dev deleted file mode 100644 index a6756ee..0000000 --- a/backend/Dockerfile.dev +++ /dev/null @@ -1,5 +0,0 @@ -FROM gradle:8.12-jdk21 AS build -WORKDIR /app -COPY . . -RUN chmod +x ./gradlew -CMD ["./gradlew", "bootRun"] \ No newline at end of file diff --git a/backend/build.gradle.kts b/backend/build.gradle.kts deleted file mode 100644 index e266b53..0000000 --- a/backend/build.gradle.kts +++ /dev/null @@ -1,32 +0,0 @@ -plugins { - kotlin("jvm") version "1.9.25" - kotlin("plugin.spring") version "1.9.25" - id("org.springframework.boot") version "3.4.1" - id("io.spring.dependency-management") version "1.1.7" -} - -group = "com.mac" -version = "0.0.1-SNAPSHOT" - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} - -repositories { - mavenCentral() -} - -dependencies { - implementation("org.springframework.boot:spring-boot-starter") - implementation("org.jetbrains.kotlin:kotlin-reflect") - implementation("org.springframework.boot:spring-boot-starter-web") - implementation("org.springframework.boot:spring-boot-starter-data-redis") -} - -kotlin { - compilerOptions { - freeCompilerArgs.addAll("-Xjsr305=strict") - } -} \ No newline at end of file diff --git a/backend/gradle/wrapper/gradle-wrapper.jar b/backend/gradle/wrapper/gradle-wrapper.jar deleted file mode 100644 index a4b76b9..0000000 Binary files a/backend/gradle/wrapper/gradle-wrapper.jar and /dev/null differ diff --git a/backend/gradle/wrapper/gradle-wrapper.properties b/backend/gradle/wrapper/gradle-wrapper.properties deleted file mode 100644 index cea7a79..0000000 --- a/backend/gradle/wrapper/gradle-wrapper.properties +++ /dev/null @@ -1,7 +0,0 @@ -distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip -networkTimeout=10000 -validateDistributionUrl=true -zipStoreBase=GRADLE_USER_HOME -zipStorePath=wrapper/dists diff --git a/backend/gradlew b/backend/gradlew deleted file mode 100755 index f5feea6..0000000 --- a/backend/gradlew +++ /dev/null @@ -1,252 +0,0 @@ -#!/bin/sh - -# -# Copyright Β© 2015-2021 the original authors. -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# -# SPDX-License-Identifier: Apache-2.0 -# - -############################################################################## -# -# Gradle start up script for POSIX generated by Gradle. -# -# Important for running: -# -# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is -# noncompliant, but you have some other compliant shell such as ksh or -# bash, then to run this script, type that shell name before the whole -# command line, like: -# -# ksh Gradle -# -# Busybox and similar reduced shells will NOT work, because this script -# requires all of these POSIX shell features: -# * functions; -# * expansions Β«$varΒ», Β«${var}Β», Β«${var:-default}Β», Β«${var+SET}Β», -# Β«${var#prefix}Β», Β«${var%suffix}Β», and Β«$( cmd )Β»; -# * compound commands having a testable exit status, especially Β«caseΒ»; -# * various built-in commands including Β«commandΒ», Β«setΒ», and Β«ulimitΒ». -# -# Important for patching: -# -# (2) This script targets any POSIX shell, so it avoids extensions provided -# by Bash, Ksh, etc; in particular arrays are avoided. -# -# The "traditional" practice of packing multiple parameters into a -# space-separated string is a well documented source of bugs and security -# problems, so this is (mostly) avoided, by progressively accumulating -# options in "$@", and eventually passing that to Java. -# -# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, -# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; -# see the in-line comments for details. -# -# There are tweaks for specific operating systems such as AIX, CygWin, -# Darwin, MinGW, and NonStop. -# -# (3) This script is generated from the Groovy template -# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt -# within the Gradle project. -# -# You can find Gradle at https://github.com/gradle/gradle/. -# -############################################################################## - -# Attempt to set APP_HOME - -# Resolve links: $0 may be a link -app_path=$0 - -# Need this for daisy-chained symlinks. -while - APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path - [ -h "$app_path" ] -do - ls=$( ls -ld "$app_path" ) - link=${ls#*' -> '} - case $link in #( - /*) app_path=$link ;; #( - *) app_path=$APP_HOME$link ;; - esac -done - -# This is normally unused -# shellcheck disable=SC2034 -APP_BASE_NAME=${0##*/} -# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) -APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s -' "$PWD" ) || exit - -# Use the maximum available, or set MAX_FD != -1 to use that value. -MAX_FD=maximum - -warn () { - echo "$*" -} >&2 - -die () { - echo - echo "$*" - echo - exit 1 -} >&2 - -# OS specific support (must be 'true' or 'false'). -cygwin=false -msys=false -darwin=false -nonstop=false -case "$( uname )" in #( - CYGWIN* ) cygwin=true ;; #( - Darwin* ) darwin=true ;; #( - MSYS* | MINGW* ) msys=true ;; #( - NONSTOP* ) nonstop=true ;; -esac - -CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar - - -# Determine the Java command to use to start the JVM. -if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD=$JAVA_HOME/jre/sh/java - else - JAVACMD=$JAVA_HOME/bin/java - fi - if [ ! -x "$JAVACMD" ] ; then - die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -else - JAVACMD=java - if ! command -v java >/dev/null 2>&1 - then - die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. - -Please set the JAVA_HOME variable in your environment to match the -location of your Java installation." - fi -fi - -# Increase the maximum file descriptors if we can. -if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then - case $MAX_FD in #( - max*) - # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - MAX_FD=$( ulimit -H -n ) || - warn "Could not query maximum file descriptor limit" - esac - case $MAX_FD in #( - '' | soft) :;; #( - *) - # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. - # shellcheck disable=SC2039,SC3045 - ulimit -n "$MAX_FD" || - warn "Could not set maximum file descriptor limit to $MAX_FD" - esac -fi - -# Collect all arguments for the java command, stacking in reverse order: -# * args from the command line -# * the main class name -# * -classpath -# * -D...appname settings -# * --module-path (only if needed) -# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. - -# For Cygwin or MSYS, switch paths to Windows format before running java -if "$cygwin" || "$msys" ; then - APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) - CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) - - JAVACMD=$( cygpath --unix "$JAVACMD" ) - - # Now convert the arguments - kludge to limit ourselves to /bin/sh - for arg do - if - case $arg in #( - -*) false ;; # don't mess with options #( - /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath - [ -e "$t" ] ;; #( - *) false ;; - esac - then - arg=$( cygpath --path --ignore --mixed "$arg" ) - fi - # Roll the args list around exactly as many times as the number of - # args, so each arg winds up back in the position where it started, but - # possibly modified. - # - # NB: a `for` loop captures its iteration list before it begins, so - # changing the positional parameters here affects neither the number of - # iterations, nor the values presented in `arg`. - shift # remove old arg - set -- "$@" "$arg" # push replacement arg - done -fi - - -# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' - -# Collect all arguments for the java command: -# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, -# and any embedded shellness will be escaped. -# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be -# treated as '${Hostname}' itself on the command line. - -set -- \ - "-Dorg.gradle.appname=$APP_BASE_NAME" \ - -classpath "$CLASSPATH" \ - org.gradle.wrapper.GradleWrapperMain \ - "$@" - -# Stop when "xargs" is not available. -if ! command -v xargs >/dev/null 2>&1 -then - die "xargs is not available" -fi - -# Use "xargs" to parse quoted args. -# -# With -n1 it outputs one arg per line, with the quotes and backslashes removed. -# -# In Bash we could simply go: -# -# readarray ARGS < <( xargs -n1 <<<"$var" ) && -# set -- "${ARGS[@]}" "$@" -# -# but POSIX shell has neither arrays nor command substitution, so instead we -# post-process each arg (as a line of input to sed) to backslash-escape any -# character that might be a shell metacharacter, then use eval to reverse -# that process (while maintaining the separation between arguments), and wrap -# the whole thing up as a single "set" statement. -# -# This will of course break if any of these variables contains a newline or -# an unmatched quote. -# - -eval "set -- $( - printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | - xargs -n1 | - sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | - tr '\n' ' ' - )" '"$@"' - -exec "$JAVACMD" "$@" diff --git a/backend/settings.gradle.kts b/backend/settings.gradle.kts deleted file mode 100644 index 25d39f4..0000000 --- a/backend/settings.gradle.kts +++ /dev/null @@ -1 +0,0 @@ -rootProject.name = "mploy" diff --git a/backend/src/main/kotlin/com/mac/mploy/MployApplication.kt b/backend/src/main/kotlin/com/mac/mploy/MployApplication.kt deleted file mode 100644 index d38fac5..0000000 --- a/backend/src/main/kotlin/com/mac/mploy/MployApplication.kt +++ /dev/null @@ -1,11 +0,0 @@ -package com.mac.mploy - -import org.springframework.boot.autoconfigure.SpringBootApplication -import org.springframework.boot.runApplication - -@SpringBootApplication -class MployApplication - -fun main(args: Array) { - runApplication(*args) -} diff --git a/backend/src/main/kotlin/com/mac/mploy/config/RedisConfig.kt b/backend/src/main/kotlin/com/mac/mploy/config/RedisConfig.kt deleted file mode 100644 index 40359c6..0000000 --- a/backend/src/main/kotlin/com/mac/mploy/config/RedisConfig.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.mac.mploy.config - -import org.springframework.beans.factory.annotation.Value -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration -import org.springframework.data.redis.connection.RedisStandaloneConfiguration -import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory -import org.springframework.data.redis.core.StringRedisTemplate - -@Configuration -class RedisConfig { - @Value("\${spring.redis.host}") - private lateinit var redisHost: String - - @Value("\${spring.redis.port}") - private var redisPort: Int = 0 - - @Bean - fun lettuceConnectionFactory(): LettuceConnectionFactory { - val configuration = RedisStandaloneConfiguration(redisHost, redisPort) - return LettuceConnectionFactory(configuration) - } - - @Bean - fun stringRedisTemplate(connectionFactory: LettuceConnectionFactory): StringRedisTemplate { - val template = StringRedisTemplate() - template.connectionFactory = connectionFactory - return template - } -} \ No newline at end of file diff --git a/backend/src/main/kotlin/com/mac/mploy/controller/HelloController.kt b/backend/src/main/kotlin/com/mac/mploy/controller/HelloController.kt deleted file mode 100644 index ac9f4a8..0000000 --- a/backend/src/main/kotlin/com/mac/mploy/controller/HelloController.kt +++ /dev/null @@ -1,30 +0,0 @@ -package com.mac.mploy.controller - -import org.slf4j.LoggerFactory -import org.springframework.beans.factory.annotation.Value -import org.springframework.data.redis.core.StringRedisTemplate -import org.springframework.web.bind.annotation.* - -@RestController -@CrossOrigin(origins = ["http://localhost:3000"]) -@RequestMapping("/api") -class HelloController( - private val redis: StringRedisTemplate, - @Value("\${spring.redis.host}") private val redisHost: String, - @Value("\${spring.redis.port}") private val redisPort: Int -) { - private val logger = LoggerFactory.getLogger(HelloController::class.java) - - @GetMapping("/hello") - fun hello(): String { - logger.info("Attempting to connect to Redis at {}:{}", redisHost, redisPort) - try { - val value = redis.opsForValue().get("greeting") - logger.info("Successfully connected to Redis. Value: {}", value) - return value ?: "Hello from Redis!" - } catch (e: Exception) { - logger.error("Failed to connect to Redis", e) - throw e - } - } -} \ No newline at end of file diff --git a/backend/src/main/resources/application.properties b/backend/src/main/resources/application.properties deleted file mode 100644 index 91ff81b..0000000 --- a/backend/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -spring.application.name=mploy diff --git a/backend/src/main/resources/application.yml b/backend/src/main/resources/application.yml deleted file mode 100644 index b4610fb..0000000 --- a/backend/src/main/resources/application.yml +++ /dev/null @@ -1,20 +0,0 @@ -spring: - redis: - host: ${REDIS_HOST:redis} - port: ${REDIS_PORT:6379} - database: 0 - timeout: 60000 - lettuce: - pool: - max-active: 8 - max-wait: -1 - max-idle: 8 - min-idle: 0 - -logging: - level: - org.springframework.data.redis: DEBUG - io.lettuce.core: DEBUG - -server: - port: 8080 \ No newline at end of file diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml deleted file mode 100644 index 4a97906..0000000 --- a/docker-compose.dev.yml +++ /dev/null @@ -1,41 +0,0 @@ -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 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 b/frontend/Dockerfile new file mode 100644 index 0000000..890ef68 --- /dev/null +++ b/frontend/Dockerfile @@ -0,0 +1,52 @@ +# ------------------------- +# Base +# ------------------------- +FROM node:20-alpine AS base +WORKDIR /app + +# ------------------------- +# Dependencies +# ------------------------- +FROM base AS deps +COPY package.json package-lock.json* ./ +RUN npm ci + +# ------------------------- +# Builder +# ------------------------- +FROM base AS builder +COPY --from=deps /app/node_modules ./node_modules +COPY package.json package-lock.json* ./ +COPY tsconfig.json next.config.ts ./ +COPY postcss.config.mjs tailwind.config.ts ./ +COPY public ./public +COPY src ./src +RUN npm run build + +# ------------------------- +# Runner +# ------------------------- +FROM base AS runner +WORKDIR /app + +# Default: prod, can override with `-e NODE_ENV=development` +ENV NODE_ENV=production + +# Copy runtime deps +COPY --from=deps /app/node_modules ./node_modules + +# Copy built assets +COPY --from=builder --chown=1001:1001 /app/.next/standalone ./ +COPY --from=builder --chown=1001:1001 /app/.next/static ./.next/static +COPY --from=builder --chown=1001:1001 /app/public ./public + +# Create non-root user +RUN addgroup --system --gid 1001 nodejs \ + && adduser --system --uid 1001 nextjs +USER nextjs + +EXPOSE 3000 +ENV PORT=3000 +ENV HOSTNAME=0.0.0.0 + +CMD ["npm", "start"] diff --git a/frontend/Dockerfile.dev b/frontend/Dockerfile.dev deleted file mode 100644 index 8590b6a..0000000 --- a/frontend/Dockerfile.dev +++ /dev/null @@ -1,6 +0,0 @@ -FROM node:22-alpine -WORKDIR /app -COPY package*.json ./ -RUN npm install -COPY . . -CMD ["npm", "run", "dev"] \ No newline at end of file diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index 31de0e7..0000000 --- a/frontend/README.md +++ /dev/null @@ -1,236 +0,0 @@ -# MPloy Job Board - -MPloy is a very simple job board with search, filter and just a website that displays all the jobs. There are 3 components in this: - -- Next.js is used for our frontend -- Kotlin (Springboot) REST backend -- GoLang scraper service that runs once a day to update our collection of jobs. - -## Core Features - -- Server-side rendering (where possible) with Next.js 15 App Router -- We want to build this app with modern principles like scalability, observability in mind. -- The job website will display 10 jobs at a time. users may filter these jobs based on whatever property. -- Multiple filters can be applied at once, including a text search filter. -- Desktop/mobile responsive UI: list/details on desktop, modal on mobile -- State persists in URL: search, filters, pagination (`/jobs?q=dev&location=sydney&page=2`) -- Direct job links supported (`/jobs/[id]`) -- Parallel data fetching for faster loads -- Real-time job search with debouncing -- Data refreshed daily via Go scraper -- There is around 1k jobs, each one with a size of around 4kBs. - -### Naming & File Structuring Conventions - -- Everything uses kebab-case `product-card.ts` unless theres an agreed standard e.g. `useCustomHook` for hook -- Group related components in feature directories (e.g. `components/layout/search/filter/` contains all components used for search filtering -- Most UI components are 50-150 lines of code. Keep pages thin, move complex logic to components -- Use layouts for shared UI across routes - -## State Management & Data Passing Patterns - -- Begin with simple props passing - max 2 levels of components -- When stateful logic needs to be reused, move it to custom hooks. -- When props drilling becomes cumbersome or state needs to be widely available, use Context. (e.g. the global fliter state of jobs should be context) -- Pre-fetch the data in the next job page -- Load the essential data first and display the page, while other job listings and details are being loaded. -- Implement parallel data fetching when possible - -## Next / React Features - -- Intercepting Routes: Use intercepting routes for modal-like experiences. -- Lazy Loading: use when we can defer the loading of heavy components -- Error Boundaries: define error.tsx files to catch errors to prevent the entire site from breaking -- Add boundaries for loading state -- Use useMemo for expensive calculations (e.g. filtered results) -- Use useRef to maintain filter input values - -## Mantine Usage Guidelines - -### Core Principles - -- Use Mantine components only when they provide significant value beyond basic HTML/CSS -- Prefer simple HTML/CSS for basic layout and text elements -- Consider bundle size and complexity impact - -## Frontend Structure - -``` -β”œβ”€β”€ next.config.ts # Next.js configuration, API routes, environment -β”œβ”€β”€ src -β”‚ β”œβ”€β”€ app -β”‚ β”‚ β”œβ”€β”€ error.tsx # Global error boundary UI -β”‚ β”‚ β”œβ”€β”€ jobs -β”‚ β”‚ β”‚ β”œβ”€β”€ [id] # Dynamic route for individual job pages -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ @modal # Intercepted route - shows job details as modal on mobile -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ page.tsx # Individual job page UI -β”‚ β”‚ β”‚ β”œβ”€β”€ error.tsx # Job section error boundary -β”‚ β”‚ β”‚ β”œβ”€β”€ layout.tsx # Job section layout wrapper (includes JobsProvider) -β”‚ β”‚ β”‚ β”œβ”€β”€ loading.tsx # Job section loading state -β”‚ β”‚ β”‚ β”œβ”€β”€ page.tsx # Main jobs listing page -β”‚ β”‚ β”œβ”€β”€ layout.tsx # Root layout with nav and theme providers -β”‚ β”‚ β”œβ”€β”€ page.tsx # Home page (redirects to /jobs) -β”‚ β”œβ”€β”€ components -β”‚ β”‚ β”œβ”€β”€ jobs -β”‚ β”‚ β”‚ β”œβ”€β”€ details -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ job-card.tsx # Individual job preview card -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ job-details.tsx # Full job details view -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ job-list.tsx # Container for job cards with virtualization -β”‚ β”‚ β”‚ β”œβ”€β”€ filters -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ dropdown-filter.tsx # Reusable filter dropdown -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ dropdown-sort.tsx # Sort options dropdown -β”‚ β”‚ β”‚ β”‚ β”œβ”€β”€ filter-section.tsx # Container for all filters -β”‚ β”‚ β”‚ β”œβ”€β”€ search -β”‚ β”‚ β”‚ β”‚ └── search-bar.tsx # Search input with suggestions -β”‚ β”‚ β”œβ”€β”€ layout -β”‚ β”‚ β”‚ └── logo.tsx # Site logo component -β”‚ β”‚ β”‚ └── nav-bar.tsx # Navigation bar -β”‚ β”œβ”€β”€ context -β”‚ β”‚ β”œβ”€β”€ jobs -β”‚ β”‚ β”‚ └── jobs-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 -β”‚ β”‚ β”œβ”€β”€ use-job-search.ts # Search functionality and API calls -β”‚ β”‚ β”œβ”€β”€ use-pagination.ts # Pagination state and navigation -β”‚ β”‚ β”œβ”€β”€ use-url-state.ts # URL parameters sync with app state -β”‚ β”œβ”€β”€ lib -β”‚ β”‚ β”œβ”€β”€ theme.ts # Mantine theme configuration -β”‚ β”œβ”€β”€ types -β”‚ β”‚ └── api.ts # API response/request types -β”‚ β”‚ └── filters.ts # Filter option types -β”‚ β”‚ └── job.ts # Job data types -β”œβ”€β”€ tailwind.config.ts # Tailwind CSS configuration -``` - -## Custom Hooks and Context - -### `useUrlState` - -```ts -// URL structure: /jobs?q=developer&location=sydney&page=1 -const { updateUrlState, getStateFromUrl } = useUrlState(); - -// All filter changes update URL automatically -updateUrlState({ search: "developer", location: "sydney" }); - -// URL state is initial source of truth on page load -const initialState = getStateFromUrl(); -``` - -### `useJobsContext` - -```ts -// Jobs context provides centralized state management -const { state, updateFilters, setSelectedJob, clearFilters } = useJobsContext(); - -// Access jobs data and loading state -const { jobs, isLoading, selectedJobId, totalJobs } = state; - -// Update filters (automatically syncs with URL) -updateFilters({ search: "developer" }); -``` - -### `useJobSearch()` and `useJobFilters()` - -```ts -// Debounced search with automatic API calls -const { searchJobs, debouncedSearch } = useJobSearch(); - -// Filter management with URL sync -const { handleFilterChange, handleClearFilters } = useJobFilters(); - -// Update multiple filters -handleFilterChange({ - locations: ["sydney"], - jobTypes: ["full-time"], -}); -``` - -### Pagination and Cache - -```ts -// Managed by usePagination hook -const { currentPage, nextPage, prevPage } = usePagination({ - totalItems: totalJobs, - itemsPerPage: 10, -}); - -// useJobSearch handles prefetching next page -useEffect(() => { - if (hasNextPage) prefetchNextPage(); -}, [currentPage]); -``` - -### Data Flow Example - -When user searches: - -1. SearchBar component calls useJobSearch().updateSearch() -2. useJobSearch updates JobsContext filters -3. useUrlState syncs new state to URL -4. useJobSearch triggers API call with new filters -5. Results update in JobsContext -6. JobList component re-renders with new data - -When user opens job details: - -1. JobCard calls selectJob from context -2. URL updates to include selectedJobId -3. JobDetails component renders selected job -4. On mobile, modal route is intercepted - -```ts -// 1. User searches for jobs -function SearchBar() { - const { handleFilterChange } = useJobFilters(); - - return ( - handleFilterChange({ search: e.target.value })} - /> - ); -} - -// 2. URL updates and jobs are fetched -function JobList() { - const { state: { jobs, isLoading } } = useJobsContext(); - - if (isLoading) return ; - - return jobs.map(job => ); -} - -// 3. User selects a job -function JobCard({ job }) { - const { setSelectedJob } = useJobsContext(); - - return ( -
setSelectedJob(job.id)}> - {job.title} -
- ); -} - -// 4. Job details display with URL sync -function JobDetails() { - const { state: { selectedJobId, jobs } } = useJobsContext(); - const job = jobs.find(j => j.id === selectedJobId); - - return job ? : null; -} -``` - -### Suggested AI Prompt - -``` -MPloy is a very simple job board with search, filter and just a website that displays all the jobs. More details about the project including coding guidelines and suggestions are in the README.md file. This should be strictly followed. - -Attached is our project code. All code prefixed with frontend contains the code for the next.js codebase. The README contains coding guidelines and they should be followed at all times. - -Help answer questions regarding this codebase. Unless otherwise specified, show only the changes the user needs to apply and include the directory of where the changes need to be applied as a comment. - -Consider whether components should be client or server sided, and ensure client sided components are marked with "use client" - -Always explain your thought process and await a go ahead before starting to solve a problem. If any prompt is unclear to you ask the user to clarify. -``` diff --git a/frontend/next.config.ts b/frontend/next.config.ts index e9ffa30..68a6c64 100644 --- a/frontend/next.config.ts +++ b/frontend/next.config.ts @@ -1,7 +1,7 @@ import type { NextConfig } from "next"; const nextConfig: NextConfig = { - /* config options here */ + output: "standalone", }; export default nextConfig; diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 068f470..d0e29b8 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -8,24 +8,37 @@ "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", + "pino": "^9.11.0", + "pino-pretty": "^13.1.1", "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 +47,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 +55,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 +80,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 +243,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 +257,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 +281,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 +305,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 +315,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 +325,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 +342,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 +351,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 +361,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 +376,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 +388,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 +444,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 +822,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 +839,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 +853,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 +862,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 +871,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 +884,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 +897,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 +962,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 +978,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 +994,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 +1010,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 +1026,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 +1042,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 +1058,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 +1074,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 +1089,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 +1119,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 +1128,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 +1151,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 +1187,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 +1212,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 +1262,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 +1291,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 +1344,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 +1369,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 +1387,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 +1411,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 +1425,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 +1508,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 +1532,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 +1549,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 +1645,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 +1675,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 +1687,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 +1702,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 +1721,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 +1904,31 @@ "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/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, "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 +1969,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 +1998,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 +2006,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 +2090,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 +2136,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 +2160,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 +2178,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 +2201,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 +2213,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 +2226,28 @@ "simple-swizzle": "^0.2.2" } }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "license": "MIT" + }, + "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 +2264,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 +2278,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 +2286,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 +2312,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", @@ -1993,11 +2379,19 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/dateformat": { + "version": "4.6.3", + "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-4.6.3.tgz", + "integrity": "sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==", + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/debug": { "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 +2405,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 +2454,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 +2476,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 +2504,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,16 +2542,23 @@ "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/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.18.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.0.tgz", @@ -2139,6 +2573,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 +2770,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 +2794,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 +2831,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 +3133,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": { @@ -2779,6 +3226,12 @@ "node": ">=0.10.0" } }, + "node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==", + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -2830,11 +3283,25 @@ "dev": true, "license": "MIT" }, + "node_modules/fast-redact": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/fast-redact/-/fast-redact-3.5.0.tgz", + "integrity": "sha512-dwsoQlS7h9hMeYUq1W++23NDcBLV4KqONnITDV9DjfS3q1SgDGVrBdvvTLUotWtPSD7asWDV9/CmsZPy8Hf70A==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-safe-stringify": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz", + "integrity": "sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==", + "license": "MIT" + }, "node_modules/fastq": { "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 +3324,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 +3371,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 +3402,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 +3434,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 +3499,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 +3537,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 +3553,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 +3573,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 +3585,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 +3594,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 +3747,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 +3755,62 @@ "node": ">= 0.4" } }, + "node_modules/help-me": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/help-me/-/help-me-5.0.0.tgz", + "integrity": "sha512-7xgomUX6ADmcYzFik0HzAxh/73YlKR9bmFzf51CZwR+b6YtzU2m0u49hQCqV6SvlqIqsaxovfwdvbnsw3b/zpg==", + "license": "MIT" + }, + "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 +3889,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 +3928,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 +3980,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 +4030,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 +4055,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 +4083,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 +4108,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 +4130,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 +4292,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 +4329,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,17 +4344,24 @@ "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" } }, + "node_modules/joycon": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/joycon/-/joycon-3.1.1.tgz", + "integrity": "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true, "license": "MIT" }, "node_modules/js-yaml": { @@ -3823,6 +4377,46 @@ "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": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", @@ -3921,7 +4515,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 +4527,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 +4545,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 +4579,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 +4591,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 +4610,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 +4619,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", @@ -4031,7 +4657,6 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", - "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" @@ -4041,24 +4666,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 +4770,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 +4790,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 +4855,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 +4879,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" @@ -4315,6 +4996,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", @@ -4387,7 +5086,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 +5101,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 +5127,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 +5136,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", @@ -4443,44 +5150,114 @@ "engines": { "node": ">=16 || 14 >=14.18" }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pino": { + "version": "9.11.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-9.11.0.tgz", + "integrity": "sha512-+YIodBB9sxcWeR8PrXC2K3gEDyfkUuVEITOcbqrfcj+z5QW4ioIcqZfYFbrLTYLsmAwunbS7nfU/dpBB6PZc1g==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0", + "fast-redact": "^3.1.1", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-pretty": { + "version": "13.1.1", + "resolved": "https://registry.npmjs.org/pino-pretty/-/pino-pretty-13.1.1.tgz", + "integrity": "sha512-TNNEOg0eA0u+/WuqH0MH0Xui7uqVk9D74ESOpjtebSQYbNWJk/dIxCXIxFsNfeN53JmtWqYHP2OrIZjT/CBEnA==", + "license": "MIT", + "dependencies": { + "colorette": "^2.0.7", + "dateformat": "^4.6.3", + "fast-copy": "^3.0.2", + "fast-safe-stringify": "^2.1.1", + "help-me": "^5.0.0", + "joycon": "^3.1.1", + "minimist": "^1.2.6", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pump": "^3.0.0", + "secure-json-parse": "^4.0.0", + "sonic-boom": "^4.0.1", + "strip-json-comments": "^5.0.2" + }, + "bin": { + "pino-pretty": "bin.js" } }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, + "node_modules/pino-pretty/node_modules/strip-json-comments": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-5.0.3.tgz", + "integrity": "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==", "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=14.16" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/pify": { - "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" - } + "node_modules/pino-std-serializers": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.0.0.tgz", + "integrity": "sha512-e906FRY0+tV27iq4juKzSYPbUj2do2X2JX4EzSca1631EB2QJQUqGbDuERal7LCtOpxl6x3+nvo9NPZcmjkiFA==", + "license": "MIT" }, "node_modules/pirates": { "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 +5274,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 +5291,7 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", @@ -4528,7 +5305,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 +5322,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 +5341,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 +5377,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 +5399,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 +5425,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 +5438,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 +5452,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 +5468,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 +5481,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": { @@ -4722,11 +5496,26 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/process-warning": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", + "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/prop-types": { "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", @@ -4734,11 +5523,20 @@ "react-is": "^16.13.1" } }, + "node_modules/pump": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -4748,7 +5546,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", @@ -4765,6 +5562,12 @@ ], "license": "MIT" }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, "node_modules/react": { "version": "19.0.0", "resolved": "https://registry.npmjs.org/react/-/react-19.0.0.tgz", @@ -4790,28 +5593,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 +5634,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 +5656,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 +5678,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 +5691,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 +5720,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" @@ -4907,6 +5728,15 @@ "node": ">=8.10.0" } }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, "node_modules/reflect.getprototypeof": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", @@ -4933,7 +5763,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 +5791,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 +5831,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,12 +5921,55 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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", "integrity": "sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==", "license": "MIT" }, + "node_modules/secure-json-parse": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-4.0.0.tgz", + "integrity": "sha512-dxtLJO6sc35jWidmLxo7ij+Eg48PM/kleBsxpC8QJE0qJICe+KawkDQmvCMZUr9u7WKVHgMW6vy3fQ7zMiFZMA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/semver": { "version": "7.6.3", "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", @@ -5199,7 +6076,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 +6088,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 +6173,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" @@ -5317,6 +6191,15 @@ "is-arrayish": "^0.3.1" } }, + "node_modules/sonic-boom": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.0.tgz", + "integrity": "sha512-INb7TM37/mAcsGmc9hyyI6+QR3rR1zVRu36B0NeGXKnOOLiZOfER5SA+N7X7k3yUYRzLWafduTDvJAfDswwEww==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -5326,6 +6209,24 @@ "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/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, "node_modules/stable-hash": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.4.tgz", @@ -5345,7 +6246,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 +6264,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 +6278,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 +6287,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 +6418,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 +6434,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 +6446,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 +6501,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 +6524,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 +6553,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 +6561,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 +6614,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 +6630,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 +6652,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 +6661,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 +6669,43 @@ "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/thread-stream": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", + "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "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 +6714,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 +6755,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 +6793,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 +6913,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 +6933,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 +6954,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 +6968,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 +6982,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 +6999,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 +7021,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 +7195,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 +7213,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 +7230,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 +7239,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 +7259,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 +7271,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 +7279,52 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "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..8f22912 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,24 +10,37 @@ "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", + "pino": "^9.11.0", + "pino-pretty": "^13.1.1", "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/public/OgImage.png b/frontend/public/OgImage.png new file mode 100644 index 0000000..fc6cbd8 Binary files /dev/null and b/frontend/public/OgImage.png differ diff --git a/frontend/public/mac.svg b/frontend/public/mac.svg new file mode 100644 index 0000000..a5e9ec5 --- /dev/null +++ b/frontend/public/mac.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/app/actions.ts b/frontend/src/app/actions.ts new file mode 100644 index 0000000..3052f0e --- /dev/null +++ b/frontend/src/app/actions.ts @@ -0,0 +1,93 @@ +// frontend/src/app/actions/feedback.ts +"use server"; + +import logger from "@/lib/logger"; + +// 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) { + logger.error("NOTION_DATABASE_ID environment variable is not set"); + throw new Error("NOTION_DATABASE_ID environment variable is not set"); + } + + if (!notionApiKey) { + logger.error("NOTION_API_KEY environment variable is not set"); + 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(); + logger.error( + { error: errorData, status: response.status }, + "Notion API error during feedback submission", + ); + return { + success: false, + message: `We're sorry, but there was an issue submitting your feedback. Please try again later.`, + }; + } + + logger.info( + { email: data.email || "Anonymous" }, + "Feedback submitted successfully", + ); + return { + success: true, + message: "Thank you! Your feedback has been submitted successfully.", + }; + } catch (error) { + logger.error(error, "Unexpected error during feedback submission"); + return { + success: false, + message: `An unexpected error occurred while submitting your feedback. Please try again.`, + }; + } +} diff --git a/frontend/src/app/error.tsx b/frontend/src/app/error.tsx index 63a13bc..86dec9f 100644 --- a/frontend/src/app/error.tsx +++ b/frontend/src/app/error.tsx @@ -1,4 +1,30 @@ "use client"; -export default function Error() { - return
Error
; + +import { useEffect } from "react"; +import { Text, Button } from "@mantine/core"; + +export default function Error({ + error, + reset, +}: { + error: Error & { digest?: string }; + reset: () => void; +}) { + useEffect(() => { + console.error("Global error:", { + message: error.message, + stack: error.stack, + digest: error.digest, + }); + }, [error]); + + return ( +
+ + Oops! Something went wrong. Please try refreshing the page or contact + support if the issue persists. + + +
+ ); } 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..187a5a6 100644 --- a/frontend/src/app/jobs/[id]/page.tsx +++ b/frontend/src/app/jobs/[id]/page.tsx @@ -1,3 +1,67 @@ -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"; + +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.png", + 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..2f183cf --- /dev/null +++ b/frontend/src/app/jobs/actions.ts @@ -0,0 +1,213 @@ +// /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"; +import logger from "@/lib/logger"; + +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) { + logger.error("MONGODB_URI environment variable is not set"); + throw new Error( + "MongoDB URI is not configured. Please check environment variables.", + ); + } + const client = new MongoClient(process.env.MONGODB_URI); + try { + await client.connect(); + logger.debug("MongoDB connected successfully"); + return await callback(client); + } catch (error) { + logger.error(error, "Failed to connect to MongoDB or execute callback"); + throw error; + } 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 }> { + logger.info( + { filters, minSponsors, prioritySponsors }, + "Fetching jobs with filters", + ); + 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; + + try { + 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), + ]); + logger.debug({ total }, "Fetched non-sponsored jobs"); + return { + jobs: (jobs as MongoJob[]) + .map(serializeJob) + .map((job) => ({ ...job, highlight: false })), + total, + }; + } else { + const sponsoredQuery = { ...query, is_sponsored: true }; + + let sponsoredJobs = await collection + .aggregate([ + { $match: sponsoredQuery }, + { $sample: { size: minSponsors * 8 } }, + ]) + .toArray(); + + sponsoredJobs = sponsoredJobs + .filter((job) => { + const isPriority = prioritySponsors.includes(job.company.name); + return isPriority ? Math.random() < 0.65 : Math.random() >= 0.35; + }) + .slice(0, minSponsors) + .map((job) => ({ ...job, highlight: true })); + + const sponsoredJobIds = sponsoredJobs.map((job) => job._id); + + const filteredQuery = { ...query, _id: { $nin: sponsoredJobIds } }; + + const [otherJobs, total] = await Promise.all([ + collection + .find(filteredQuery) + .sort({ created_at: -1 }) + .skip(skip) + .limit(PAGE_SIZE - sponsoredJobs.length) + .toArray(), + collection.countDocuments(query), + ]); + + const mergedJobs = [ + ...sponsoredJobs.map((job) => ({ ...job, highlight: true })), + ...otherJobs.map((job) => ({ ...job, highlight: false })), + ].slice(0, PAGE_SIZE); + + logger.debug( + { + sponsoredCount: sponsoredJobs.length, + otherCount: otherJobs.length, + total, + }, + "Fetched sponsored and other jobs", + ); + return { + jobs: (mergedJobs as MongoJob[]).map(serializeJob), + total, + }; + } + } catch (error) { + logger.error({ query, filters }, "Error fetching jobs"); + throw error; + } + }); +} + +/** + * Fetches a single job by its id. + */ +export async function getJobById(id: string): Promise { + logger.info({ id }, "Fetching job by ID"); + 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) { + logger.warn({ id }, "Job not found"); + return null; + } + logger.debug({ id }, "Job fetched successfully"); + return serializeJob(job as MongoJob); + }); +} diff --git a/frontend/src/app/jobs/error.tsx b/frontend/src/app/jobs/error.tsx index 364eeb9..ee5432c 100644 --- a/frontend/src/app/jobs/error.tsx +++ b/frontend/src/app/jobs/error.tsx @@ -1,4 +1,31 @@ +// 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("Jobs page error:", { + message: error.message, + stack: error.stack, + digest: error.digest, + }); + }, [error]); + + return ( +
+ + Oops! Something went wrong while loading the jobs. Please try refreshing + the page. + + +
+ ); } 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..dbaf664 100644 --- a/frontend/src/app/layout.tsx +++ b/frontend/src/app/layout.tsx @@ -1,25 +1,75 @@ -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 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.png", + 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/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..55a58c3 100644 --- a/frontend/src/components/layout/logo.tsx +++ b/frontend/src/components/layout/logo.tsx @@ -1,5 +1,17 @@ -import { Text } from "@mantine/core"; +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 ( -