From c42af38da2d9f7d46544252e55e5e89ae008ff32 Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 13 May 2026 13:32:53 +0900 Subject: [PATCH 001/103] =?UTF-8?q?init:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=B4=88=EA=B8=B0=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 41 + README.md | 101 +- eslint.config.mjs | 18 + next.config.ts | 8 + package.json | 27 + pnpm-lock.yaml | 4082 +++++++++++++++++++++++++++++++++++++++++++ pnpm-workspace.yaml | 6 + postcss.config.mjs | 7 + public/file.svg | 1 + public/globe.svg | 1 + public/next.svg | 1 + public/vercel.svg | 1 + public/window.svg | 1 + src/app/favicon.ico | Bin 0 -> 25931 bytes src/app/globals.css | 26 + src/app/layout.tsx | 33 + src/app/page.tsx | 65 + tsconfig.json | 34 + 18 files changed, 4375 insertions(+), 78 deletions(-) create mode 100644 .gitignore create mode 100644 eslint.config.mjs create mode 100644 next.config.ts create mode 100644 package.json create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml create mode 100644 postcss.config.mjs create mode 100644 public/file.svg create mode 100644 public/globe.svg create mode 100644 public/next.svg create mode 100644 public/vercel.svg create mode 100644 public/window.svg create mode 100644 src/app/favicon.ico create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/page.tsx create mode 100644 tsconfig.json diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ef6a52 --- /dev/null +++ b/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/README.md b/README.md index a124ccb..e215bc4 100644 --- a/README.md +++ b/README.md @@ -1,91 +1,36 @@ -# next-vote-23rd +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). -## **서론** +## Getting Started -안녕하세요! 프론트엔드 운영진 원채영입니다. 🍀 +First, run the development server: -벌써 마지막 과제에 도달했네요. 이번 스터디의 마지막 과제는 특별히 **백엔드 팀원들과 함께하는 협업 과제**로 준비했습니다! +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` -현대 웹 개발에서는 REST API 기반의 데이터 통신이 핵심이 되면서, 프론트엔드와 백엔드 간의 협업이 더욱 중요해지고 있습니다. 백엔드는 API를 통해 데이터를 제공하고, 프론트엔드는 이를 바탕으로 사용자 경험을 구현합니다. 따라서 API를 이해하고 활용하는 능력, 그리고 백엔드 개발자와 원활하게 소통하는 경험은 프론트엔드 개발자에게 꼭 필요한 역량입니다. +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. -이번 과제는 앞으로 진행될 팀 프로젝트에 앞서, 실제 협업 방식을 미리 경험해보는 연습 과정이라고 생각해주시면 좋을 것 같습니다. Next.js를 활용해 **투표 기능이 포함된 애플리케이션**을 제작하며, 백엔드와의 소통 방식, 역할 분담, 데이터 흐름 등을 직접 경험해보세요! +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. -이번 과제를 통해 단순히 기능 구현을 넘어, 함께 서비스를 만들어가는 협업 경험까지 얻어가셨으면 좋겠습니다. +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. -여러분의 멋진 결과물을 기대하겠습니다. 마지막 과제까지 모두 화이팅입니다! 🔥 +## Learn More ---- +To learn more about Next.js, take a look at the following resources: -## **과제** +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. -### **목표** +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! -- REST API를 활용하여 서버와의 통신 방식을 이해합니다. -- JavaScript의 비동기 처리 방식(`async/await`, Promise)을 익힙니다. -- API 문서를 바탕으로 백엔드와 소통하는 방법을 학습합니다. -- 팀 내 협업을 통해 효율적인 역할 분담을 고민하고 적용합니다. +## Deploy on Vercel ---- +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. -### **기한** - -- **2026년 5월 16일 토요일**까지 1차 필수 구현 사항이 적용된 중간 결과물을 제출해주세요. - ---- - -### **1차 필수 구현 사항** - -1. **프로젝트 세팅** - - Next.js의 특성을 고려하여 효과적인 폴더 구조를 고민해 봅니다. - - API 통신, 스타일링, 전역 상태 관리 및 기타 라이브러리 등을 팀원과 상의하여 세팅합니다. - - **Github organization을 사용합니다.** -2. **퍼블리싱** - - 프로젝트에 필요한 모든 화면을 퍼블리싱합니다. - - 다양한 디바이스에서 최적의 사용자 경험을 제공하기 위해 반응형 디자인을 적용합니다. -3. **로그인 기능** - - 사용자는 아이디와 비밀번호를 입력하여 로그인할 수 있습니다. - - 로그인 시 JWT를 통해 인증을 처리합니다. - - 아이디 또는 비밀번호가 틀렸을 경우, 에러 메시지를 표시합니다. - - 로그아웃 기능을 구현합니다. - - **백에서 서버 배포가 안 되었을 경우**에는 다음 주로 넘겨도 괜찮습니다. - - -### **2차 필수 구현 사항** - -1. **투표 기능** - - 로그인한 사용자는 투표에 참여할 수 있습니다. - - 각 후보에 대한 투표 수를 실시간으로 확인할 수 있습니다. - - 사용자는 한 번만 투표할 수 있으며, 중복 투표를 방지합니다. -2. **후보 목록 조회** - - 모든 사용자는 후보자의 목록과 상세 정보를 확인할 수 있습니다. - - 후보자의 이름, 사진, 소개 등을 표시합니다. -3. **투표 결과 조회** - - 투표 종료 후, 모든 사용자는 최종 투표 결과를 확인할 수 있습니다. - - 각 후보자의 득표 수와 득표율을 시각적으로 표현합니다. -4. **에러 처리** - - 서버 오류, 네트워크 문제 등 다양한 에러 상황에 대한 처리를 구현합니다. - - 사용자에게 이해하기 쉬운 에러 메시지를 제공합니다. - ---- - -### **디자인 참고** - -다음의 리소스를 참고해 UI/UX를 개선해보세요: - -- [디자인 레퍼런스1](https://www.figma.com/design/7xoPYTjMHcwPk2yl92Eynx/%ED%98%91%EB%8F%99%EA%B3%BC%EC%A0%9C-%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4?node-id=0-1&node-type=canvas) -- [디자인 레퍼런스2](https://www.figma.com/design/XpKkyWcguIFY9QzWWJHOyL/%ED%98%91%EB%8F%99%EA%B3%BC%EC%A0%9C-%EB%A0%88%ED%8D%BC%EB%9F%B0%EC%8A%A4?node-id=0-1) -- [디자인 레퍼런스3](https://www.figma.com/design/12WK4MEhjwNmt89HkRu8Gp/%EB%B0%94%EB%A6%AC%EB%B0%94%EB%A6%AC-%ED%88%AC%ED%91%9C) -- [디자인 레퍼런스4](https://www.figma.com/design/qsTGeBRrKWiWE04eVOTFQ9/CEOS-CupfeeDeal-Vote?node-id=38-503&p=f&t=sM5p1Gw4hA5G5H5D-0) - -이전 기수 과제 - -- [CupfeeDeal](https://github.com/CEOS-Developers/next-vote-20th/pull/6) -- [하니홈](https://github.com/CEOS-Developers/next-vote-21th/pull/5) -- [모앤디](https://github.com/CEOS-Developers/next-vote-22nd/pull/8) -- [스토릭스](https://github.com/CEOS-Developers/next-vote-22nd/pull/11) - -### **선택 사항** - -- API 요청 방식은 자유롭게 선택 가능 (예: Fetch API, axios 등). -- 최신 자바스크립트 스타일에 익숙해지기 위해 `Promise.then()` 대신 `async/await`를 사용해 보세요. -- 페이지 접근 권한 관리와 로그인 상태 검증을 **proxy.ts**(구 middleware.ts)를 활용해 서버 단(Proxy)에서 미리 처리해 보세요! (참고자료: [Next.js proxy](https://nextjs.org/docs/app/getting-started/proxy)) +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..05e726d --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,18 @@ +import { defineConfig, globalIgnores } from "eslint/config"; +import nextVitals from "eslint-config-next/core-web-vitals"; +import nextTs from "eslint-config-next/typescript"; + +const eslintConfig = defineConfig([ + ...nextVitals, + ...nextTs, + // Override default ignores of eslint-config-next. + globalIgnores([ + // Default ignores of eslint-config-next: + ".next/**", + "out/**", + "build/**", + "next-env.d.ts", + ]), +]); + +export default eslintConfig; diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 0000000..66e1566 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,8 @@ +import type { NextConfig } from "next"; + +const nextConfig: NextConfig = { + /* config options here */ + reactCompiler: true, +}; + +export default nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 0000000..e422c70 --- /dev/null +++ b/package.json @@ -0,0 +1,27 @@ +{ + "name": "next-vote-23rd", + "version": "0.1.0", + "private": true, + "scripts": { + "dev": "next dev", + "build": "next build", + "start": "next start", + "lint": "eslint" + }, + "dependencies": { + "next": "16.2.6", + "react": "19.2.4", + "react-dom": "19.2.4" + }, + "devDependencies": { + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "babel-plugin-react-compiler": "1.0.0", + "eslint": "^9", + "eslint-config-next": "16.2.6", + "tailwindcss": "^4", + "typescript": "^5" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..7569832 --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4082 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + next: + specifier: 16.2.6 + version: 16.2.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) + react: + specifier: 19.2.4 + version: 19.2.4 + react-dom: + specifier: 19.2.4 + version: 19.2.4(react@19.2.4) + devDependencies: + '@tailwindcss/postcss': + specifier: ^4 + version: 4.3.0 + '@types/node': + specifier: ^20 + version: 20.19.41 + '@types/react': + specifier: ^19 + version: 19.2.14 + '@types/react-dom': + specifier: ^19 + version: 19.2.3(@types/react@19.2.14) + babel-plugin-react-compiler: + specifier: 1.0.0 + version: 1.0.0 + eslint: + specifier: ^9 + version: 9.39.4(jiti@2.7.0) + eslint-config-next: + specifier: 16.2.6 + version: 16.2.6(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + tailwindcss: + specifier: ^4 + version: 4.3.0 + typescript: + specifier: ^5 + version: 5.9.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@babel/code-frame@7.29.0': + resolution: {integrity: sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.3': + resolution: {integrity: sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.0': + resolution: {integrity: sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.1': + resolution: {integrity: sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.28.6': + resolution: {integrity: sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.28.0': + resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.28.6': + resolution: {integrity: sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.28.6': + resolution: {integrity: sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-string-parser@7.27.1': + resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.28.5': + resolution: {integrity: sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.27.1': + resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.3': + resolution: {integrity: sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/template@7.28.6': + resolution: {integrity: sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.0': + resolution: {integrity: sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.0': + resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} + engines: {node: '>=6.9.0'} + + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} + + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/wasi-threads@1.2.1': + resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} + + '@eslint-community/eslint-utils@4.9.1': + resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.2': + resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.2': + resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.17.0': + resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.5': + resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.39.4': + resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.1': + resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.2': + resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.8': + resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} + engines: {node: '>=18.18.0'} + + '@humanfs/types@0.15.0': + resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.5': + resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.5': + resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.4': + resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.4': + resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.4': + resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-arm@1.2.4': + resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-ppc64@1.2.4': + resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-riscv64@1.2.4': + resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-s390x@1.2.4': + resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linux-x64@1.2.4': + resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-linux-arm64@0.34.5': + resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-arm@0.34.5': + resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-ppc64@0.34.5': + resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-riscv64@0.34.5': + resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-s390x@0.34.5': + resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@img/sharp-linux-x64@0.34.5': + resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@img/sharp-linuxmusl-arm64@0.34.5': + resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@img/sharp-linuxmusl-x64@0.34.5': + resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@img/sharp-wasm32@0.34.5': + resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.5': + resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.5': + resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.5': + resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@next/env@16.2.6': + resolution: {integrity: sha512-gd8HoHN4ufj73WmR3JmVolrpJR47ILK6LouP5xElPglaVxir6e1a7VzvTvDWkOoPXT9rkkTzyCxBu4yeZfZwcw==} + + '@next/eslint-plugin-next@16.2.6': + resolution: {integrity: sha512-Z8l6o4JWKUl755x4R+wogD86KPeU+Ckw4K+SYG4kHeOJtRenDeK+OSbGcqZpDtbwn9DsJVdir2UxmwXuinUbUw==} + + '@next/swc-darwin-arm64@16.2.6': + resolution: {integrity: sha512-ZJGkkcNfYgrrMkqOdZ7zoLa1TOy0qpcMfk/z4Mh/FKUz40gVO+HNQWqmLxf67Z5WB64DRp0dhEbyHfel+6sJUg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@16.2.6': + resolution: {integrity: sha512-v/YLBHIY132Ced3puBJ7YJKw1lqsCrgcNo2aRJlCEyQrrCeRJlvGlnmxhPxNQI3KE3N1DN5r9TPNPvka3nq5RQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@16.2.6': + resolution: {integrity: sha512-RPOvqlYBbcQjkz9VQQDZ2T2bARIjXZV1KFlt+V2Mr6SW/e4I9fcKsaA0hdyf2FHoTlsV2xnBd5Y912rP/1Ce6w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-arm64-musl@16.2.6': + resolution: {integrity: sha512-URUTu1+dMkxJsPFgm+OeEvq9wf5sujw0EvgYy80TDGHTSLTnIHeqb0Eu8A3sC95IRgjejQL+kC4mw+4yPxiAXA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@next/swc-linux-x64-gnu@16.2.6': + resolution: {integrity: sha512-DOj182mPV8G3UkrayLoREM5YEYI+Dk5wv7Ox9xl1fFibAELEsFD0lDPfHIeILlutMMfdyhlzYPELG3peuKaurw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@next/swc-linux-x64-musl@16.2.6': + resolution: {integrity: sha512-HKQ5SP/V/ub73UvF7n/zeJlxk2kLmtL7Wzrg4WfmkjmNos5onJ2tKu7yZOPdL18A6Svfn3max29ym+ry7NkK4g==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@next/swc-win32-arm64-msvc@16.2.6': + resolution: {integrity: sha512-LZXpTlPyS5v7HhSmnvsLGP3iIYgYOBnc8r8ArlT55sGHV89bR2HlDdBjWQ+PY6SJMmk8TuVGFuxalnP3k/0Dwg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@16.2.6': + resolution: {integrity: sha512-F0+4i0h9J6C4eE3EAPWsoCk7UW/dbzOjyzxY0qnDUOYFu6FFmdZ6l97/XdV3/Nz3VYyO7UWjyEJUXkGqcoXfMA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.3.0': + resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==} + + '@tailwindcss/oxide-android-arm64@4.3.0': + resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.0': + resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.0': + resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==} + engines: {node: '>= 20'} + + '@tailwindcss/postcss@4.3.0': + resolution: {integrity: sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==} + + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.19.41': + resolution: {integrity: sha512-ECymXOukMnOoVkC2bb1Vc/w/836DXncOg5m8Xj1RH7xSHZJWNYY6Zh7EH477vcnD5egKNNfy2RpNOmuChhFPgQ==} + + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.14': + resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + + '@typescript-eslint/eslint-plugin@8.59.3': + resolution: {integrity: sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.59.3 + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/parser@8.59.3': + resolution: {integrity: sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/project-service@8.59.3': + resolution: {integrity: sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/scope-manager@8.59.3': + resolution: {integrity: sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.59.3': + resolution: {integrity: sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/type-utils@8.59.3': + resolution: {integrity: sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/types@8.59.3': + resolution: {integrity: sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.59.3': + resolution: {integrity: sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/utils@8.59.3': + resolution: {integrity: sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + '@typescript-eslint/visitor-keys@8.59.3': + resolution: {integrity: sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.11.4: + resolution: {integrity: sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA==} + engines: {node: '>=4'} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + babel-plugin-react-compiler@1.0.0: + resolution: {integrity: sha512-Ixm8tFfoKKIPYdCCKYTsqv+Fd4IJ0DQqMyEimo+pxUOMUR9cVPlwTrFt9Avu+3cb6Zp3mAzl+t1MrG2fxxKsxw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + + baseline-browser-mapping@2.10.29: + resolution: {integrity: sha512-Asa2krT+XTPZINCS+2QcyS8WTkObE77RwkydwF7h6DmnKqbvlalz93m/dnphUyCa6SWSP51VgtEUf2FN+gelFQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + brace-expansion@1.1.14: + resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.9: + resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001792: + resolution: {integrity: sha512-hVLMUZFgR4JJ6ACt1uEESvQN1/dBVqPAKY0hgrV70eN3391K6juAfTjKZLKvOMsx8PxA7gsY1/tLMMTcfFLLpw==} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + electron-to-chromium@1.5.353: + resolution: {integrity: sha512-kOrWphBi8TOZyiJZqsgqIle0lw+tzmnQK83pV9dZUd01Nm2POECSyFQMAuarzZdYqQW7FH9RaYOuaRo3h+bQ3w==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.21.3: + resolution: {integrity: sha512-QyL119InA+XXEkNLNTPCXPugSvOfhwv0JOlGNzvxs0hZaiHLNvXSpudUWsOlsXGWJh8G6ckCScEkVHfX3kw/2Q==} + engines: {node: '>=10.13.0'} + + es-abstract@1.24.2: + resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.3.2: + resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@16.2.6: + resolution: {integrity: sha512-z2ELYSkyrrJ6cuunTU8vhsT/RpouPkjaSah06nVW6Rg2Hpg0Vs8s497/e5s8G8qtdp4ccsiovz5P1rv+5VSW2Q==} + peerDependencies: + eslint: '>=9.0.0' + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-import-resolver-node@0.3.10: + resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-react-hooks@7.1.1: + resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} + engines: {node: '>=18'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@5.0.1: + resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} + engines: {node: ^20.19.0 || ^22.13.0 || >=24} + + eslint@9.39.4: + resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.7.0: + resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.4.2: + resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.14.0: + resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globals@16.4.0: + resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.3: + resolution: {integrity: sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==} + engines: {node: '>= 0.4'} + + hermes-estree@0.25.1: + resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} + + hermes-parser@0.25.1: + resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.1: + resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + hasBin: true + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + + minimatch@3.1.5: + resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next@16.2.6: + resolution: {integrity: sha512-qOVgKJg1+At15NpeUP+eJgCHvTCgXsogweq87Ri/Ix7PkqQHg4sdaXmSFqKlgaIXE4kW0g25LE68W87UANlHtw==} + engines: {node: '>=20.9.0'} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + node-exports-info@1.6.0: + resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} + engines: {node: '>= 0.4'} + + node-releases@2.0.38: + resolution: {integrity: sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} + engines: {node: '>=8.6'} + + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.14: + resolution: {integrity: sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-dom@19.2.4: + resolution: {integrity: sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==} + peerDependencies: + react: ^19.2.4 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react@19.2.4: + resolution: {integrity: sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==} + engines: {node: '>=0.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@2.0.0-next.6: + resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + engines: {node: '>= 0.4'} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} + engines: {node: '>=0.4'} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.8.0: + resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + sharp@0.34.5: + resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.1: + resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tailwindcss@4.3.0: + resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinyglobby@0.2.16: + resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + engines: {node: '>=12.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.5.0: + resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript-eslint@8.59.3: + resolution: {integrity: sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 + typescript: '>=4.8.4 <6.1.0' + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.20: + resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zod-validation-error@4.0.2: + resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} + engines: {node: '>=18.0.0'} + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + + zod@4.4.3: + resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@babel/code-frame@7.29.0': + dependencies: + '@babel/helper-validator-identifier': 7.28.5 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.3': {} + + '@babel/core@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-compilation-targets': 7.28.6 + '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.1': + dependencies: + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.28.6': + dependencies: + '@babel/compat-data': 7.29.3 + '@babel/helper-validator-option': 7.27.1 + browserslist: 4.28.2 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.28.0': {} + + '@babel/helper-module-imports@7.28.6': + dependencies: + '@babel/traverse': 7.29.0 + '@babel/types': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.28.6(@babel/core@7.29.0)': + dependencies: + '@babel/core': 7.29.0 + '@babel/helper-module-imports': 7.28.6 + '@babel/helper-validator-identifier': 7.28.5 + '@babel/traverse': 7.29.0 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.27.1': {} + + '@babel/helper-validator-identifier@7.28.5': {} + + '@babel/helper-validator-option@7.27.1': {} + + '@babel/helpers@7.29.2': + dependencies: + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + + '@babel/parser@7.29.3': + dependencies: + '@babel/types': 7.29.0 + + '@babel/template@7.28.6': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/parser': 7.29.3 + '@babel/types': 7.29.0 + + '@babel/traverse@7.29.0': + dependencies: + '@babel/code-frame': 7.29.0 + '@babel/generator': 7.29.1 + '@babel/helper-globals': 7.28.0 + '@babel/parser': 7.29.3 + '@babel/template': 7.28.6 + '@babel/types': 7.29.0 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.0': + dependencies: + '@babel/helper-string-parser': 7.27.1 + '@babel/helper-validator-identifier': 7.28.5 + + '@emnapi/core@1.10.0': + dependencies: + '@emnapi/wasi-threads': 1.2.1 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.2.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))': + dependencies: + eslint: 9.39.4(jiti@2.7.0) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.2': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.5 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.2': + dependencies: + '@eslint/core': 0.17.0 + + '@eslint/core@0.17.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.5': + dependencies: + ajv: 6.15.0 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.1 + minimatch: 3.1.5 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.39.4': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.1': + dependencies: + '@eslint/core': 0.17.0 + levn: 0.4.1 + + '@humanfs/core@0.19.2': + dependencies: + '@humanfs/types': 0.15.0 + + '@humanfs/node@0.16.8': + dependencies: + '@humanfs/core': 0.19.2 + '@humanfs/types': 0.15.0 + '@humanwhocodes/retry': 0.4.3 + + '@humanfs/types@0.15.0': {} + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.1.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.4 + optional: true + + '@img/sharp-darwin-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.4': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.2.4': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.4': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.4': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.4': + optional: true + + '@img/sharp-linux-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.4 + optional: true + + '@img/sharp-linux-arm@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.4 + optional: true + + '@img/sharp-linux-ppc64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.4 + optional: true + + '@img/sharp-linux-riscv64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.2.4 + optional: true + + '@img/sharp-linux-s390x@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.4 + optional: true + + '@img/sharp-linux-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.5': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + optional: true + + '@img/sharp-wasm32@0.34.5': + dependencies: + '@emnapi/runtime': 1.10.0 + optional: true + + '@img/sharp-win32-arm64@0.34.5': + optional: true + + '@img/sharp-win32-ia32@0.34.5': + optional: true + + '@img/sharp-win32-x64@0.34.5': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 + optional: true + + '@next/env@16.2.6': {} + + '@next/eslint-plugin-next@16.2.6': + dependencies: + fast-glob: 3.3.1 + + '@next/swc-darwin-arm64@16.2.6': + optional: true + + '@next/swc-darwin-x64@16.2.6': + optional: true + + '@next/swc-linux-arm64-gnu@16.2.6': + optional: true + + '@next/swc-linux-arm64-musl@16.2.6': + optional: true + + '@next/swc-linux-x64-gnu@16.2.6': + optional: true + + '@next/swc-linux-x64-musl@16.2.6': + optional: true + + '@next/swc-win32-arm64-msvc@16.2.6': + optional: true + + '@next/swc-win32-x64-msvc@16.2.6': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.20.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@rtsao/scc@1.1.0': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.3.0': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.0 + + '@tailwindcss/oxide-android-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.0': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.0': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.0': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.0': + optional: true + + '@tailwindcss/oxide@4.3.0': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-arm64': 4.3.0 + '@tailwindcss/oxide-darwin-x64': 4.3.0 + '@tailwindcss/oxide-freebsd-x64': 4.3.0 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.0 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.0 + '@tailwindcss/oxide-linux-x64-musl': 4.3.0 + '@tailwindcss/oxide-wasm32-wasi': 4.3.0 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.0 + + '@tailwindcss/postcss@4.3.0': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.3.0 + '@tailwindcss/oxide': 4.3.0 + postcss: 8.5.14 + tailwindcss: 4.3.0 + + '@tybys/wasm-util@0.10.2': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/estree@1.0.9': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@20.19.41': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.3(@types/react@19.2.14)': + dependencies: + '@types/react': 19.2.14 + + '@types/react@19.2.14': + dependencies: + csstype: 3.2.3 + + '@typescript-eslint/eslint-plugin@8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/type-utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.3 + eslint: 9.39.4(jiti@2.7.0) + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.59.3 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.59.3(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.59.3': + dependencies: + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 + + '@typescript-eslint/tsconfig-utils@8.59.3(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.59.3': {} + + '@typescript-eslint/typescript-estree@8.59.3(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.59.3(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.59.3(typescript@5.9.3) + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/visitor-keys': 8.59.3 + debug: 4.4.3 + minimatch: 10.2.5 + semver: 7.8.0 + tinyglobby: 0.2.16 + ts-api-utils: 2.5.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@typescript-eslint/scope-manager': 8.59.3 + '@typescript-eslint/types': 8.59.3 + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.59.3': + dependencies: + '@typescript-eslint/types': 8.59.3 + eslint-visitor-keys: 5.0.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + acorn-jsx@5.3.2(acorn@8.16.0): + dependencies: + acorn: 8.16.0 + + acorn@8.16.0: {} + + ajv@6.15.0: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.11.4: {} + + axobject-query@4.1.0: {} + + babel-plugin-react-compiler@1.0.0: + dependencies: + '@babel/types': 7.29.0 + + balanced-match@1.0.2: {} + + balanced-match@4.0.4: {} + + baseline-browser-mapping@2.10.29: {} + + brace-expansion@1.1.14: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.28.2: + dependencies: + baseline-browser-mapping: 2.10.29 + caniuse-lite: 1.0.30001792 + electron-to-chromium: 1.5.353 + node-releases: 2.0.38 + update-browserslist-db: 1.2.3(browserslist@4.28.2) + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.9: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001792: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + client-only@0.0.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.2.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + detect-libc@2.1.2: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + electron-to-chromium@1.5.353: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.21.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-abstract@1.24.2: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.4 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.20 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.3.2: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + math-intrinsics: 1.1.0 + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.3 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + escalade@3.2.0: {} + + escape-string-regexp@4.0.0: {} + + eslint-config-next@16.2.6(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 16.2.6 + eslint: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0)) + eslint-plugin-react-hooks: 7.1.1(eslint@9.39.4(jiti@2.7.0)) + globals: 16.4.0 + typescript-eslint: 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - '@typescript-eslint/parser' + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-import-resolver-node@0.3.10: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.2 + resolve: 2.0.0-next.6 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.39.4(jiti@2.7.0) + get-tsconfig: 4.14.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.16 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.39.4(jiti@2.7.0) + eslint-import-resolver-node: 0.3.10 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)) + hasown: 2.0.3 + is-core-module: 2.16.2 + is-glob: 4.0.3 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.7.0)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.4 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.39.4(jiti@2.7.0) + hasown: 2.0.3 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.5 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)): + dependencies: + '@babel/core': 7.29.0 + '@babel/parser': 7.29.3 + eslint: 9.39.4(jiti@2.7.0) + hermes-parser: 0.25.1 + zod: 4.4.3 + zod-validation-error: 4.0.2(zod@4.4.3) + transitivePeerDependencies: + - supports-color + + eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.3.2 + eslint: 9.39.4(jiti@2.7.0) + estraverse: 5.3.0 + hasown: 2.0.3 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.5 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.6 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint-visitor-keys@5.0.1: {} + + eslint@9.39.4(jiti@2.7.0): + dependencies: + '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.2 + '@eslint/config-helpers': 0.4.2 + '@eslint/core': 0.17.0 + '@eslint/eslintrc': 3.3.5 + '@eslint/js': 9.39.4 + '@eslint/plugin-kit': 0.4.1 + '@humanfs/node': 0.16.8 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.9 + ajv: 6.15.0 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.7.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.5 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.16.0 + acorn-jsx: 5.3.2(acorn@8.16.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.7.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + esutils@2.0.3: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.20.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.4.2 + keyv: 4.5.4 + + flatted@3.4.2: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.3 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.3 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.14.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globals@16.4.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.3: + dependencies: + function-bind: 1.1.2 + + hermes-estree@0.25.1: {} + + hermes-parser@0.25.1: + dependencies: + hermes-estree: 0.25.1 + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.3 + side-channel: 1.1.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.8.0 + + is-callable@1.2.7: {} + + is-core-module@2.16.2: + dependencies: + hasown: 2.0.3 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.3 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.20 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.1: + dependencies: + argparse: 2.0.1 + + jsesc@3.1.0: {} + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + json5@2.2.3: {} + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.2 + + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + + minimatch@3.1.5: + dependencies: + brace-expansion: 1.1.14 + + minimist@1.2.8: {} + + ms@2.1.3: {} + + nanoid@3.3.12: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + next@16.2.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4): + dependencies: + '@next/env': 16.2.6 + '@swc/helpers': 0.5.15 + baseline-browser-mapping: 2.10.29 + caniuse-lite: 1.0.30001792 + postcss: 8.4.31 + react: 19.2.4 + react-dom: 19.2.4(react@19.2.4) + styled-jsx: 5.1.6(@babel/core@7.29.0)(react@19.2.4) + optionalDependencies: + '@next/swc-darwin-arm64': 16.2.6 + '@next/swc-darwin-x64': 16.2.6 + '@next/swc-linux-arm64-gnu': 16.2.6 + '@next/swc-linux-arm64-musl': 16.2.6 + '@next/swc-linux-x64-gnu': 16.2.6 + '@next/swc-linux-x64-musl': 16.2.6 + '@next/swc-win32-arm64-msvc': 16.2.6 + '@next/swc-win32-x64-msvc': 16.2.6 + babel-plugin-react-compiler: 1.0.0 + sharp: 0.34.5 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + node-exports-info@1.6.0: + dependencies: + array.prototype.flatmap: 1.3.3 + es-errors: 1.3.0 + object.entries: 1.1.9 + semver: 6.3.1 + + node-releases@2.0.38: {} + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + picocolors@1.1.1: {} + + picomatch@2.3.2: {} + + picomatch@4.0.4: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.4.31: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.14: + dependencies: + nanoid: 3.3.12 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + react-dom@19.2.4(react@19.2.4): + dependencies: + react: 19.2.4 + scheduler: 0.27.0 + + react-is@16.13.1: {} + + react@19.2.4: {} + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@2.0.0-next.6: + dependencies: + es-errors: 1.3.0 + is-core-module: 2.16.2 + node-exports-info: 1.6.0 + object-keys: 1.1.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.4: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.27.0: {} + + semver@6.3.1: {} + + semver@7.8.0: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + sharp@0.34.5: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.0 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.5 + '@img/sharp-darwin-x64': 0.34.5 + '@img/sharp-libvips-darwin-arm64': 1.2.4 + '@img/sharp-libvips-darwin-x64': 1.2.4 + '@img/sharp-libvips-linux-arm': 1.2.4 + '@img/sharp-libvips-linux-arm64': 1.2.4 + '@img/sharp-libvips-linux-ppc64': 1.2.4 + '@img/sharp-libvips-linux-riscv64': 1.2.4 + '@img/sharp-libvips-linux-s390x': 1.2.4 + '@img/sharp-libvips-linux-x64': 1.2.4 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.4 + '@img/sharp-libvips-linuxmusl-x64': 1.2.4 + '@img/sharp-linux-arm': 0.34.5 + '@img/sharp-linux-arm64': 0.34.5 + '@img/sharp-linux-ppc64': 0.34.5 + '@img/sharp-linux-riscv64': 0.34.5 + '@img/sharp-linux-s390x': 0.34.5 + '@img/sharp-linux-x64': 0.34.5 + '@img/sharp-linuxmusl-arm64': 0.34.5 + '@img/sharp-linuxmusl-x64': 0.34.5 + '@img/sharp-wasm32': 0.34.5 + '@img/sharp-win32-arm64': 0.34.5 + '@img/sharp-win32-ia32': 0.34.5 + '@img/sharp-win32-x64': 0.34.5 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.1: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.1 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + source-map-js@1.2.1: {} + + stable-hash@0.0.5: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.2 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.2 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.9 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.9 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(@babel/core@7.29.0)(react@19.2.4): + dependencies: + client-only: 0.0.1 + react: 19.2.4 + optionalDependencies: + '@babel/core': 7.29.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + tailwindcss@4.3.0: {} + + tapable@2.3.3: {} + + tinyglobby@0.2.16: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.5.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.9 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript-eslint@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3): + dependencies: + '@typescript-eslint/eslint-plugin': 8.59.3(@typescript-eslint/parser@8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3))(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/parser': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + '@typescript-eslint/typescript-estree': 8.59.3(typescript@5.9.3) + '@typescript-eslint/utils': 8.59.3(eslint@9.39.4(jiti@2.7.0))(typescript@5.9.3) + eslint: 9.39.4(jiti@2.7.0) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + update-browserslist-db@1.2.3(browserslist@4.28.2): + dependencies: + browserslist: 4.28.2 + escalade: 3.2.0 + picocolors: 1.1.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.20 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.20: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.9 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + yallist@3.1.1: {} + + yocto-queue@0.1.0: {} + + zod-validation-error@4.0.2(zod@4.4.3): + dependencies: + zod: 4.4.3 + + zod@4.4.3: {} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml new file mode 100644 index 0000000..2179a04 --- /dev/null +++ b/pnpm-workspace.yaml @@ -0,0 +1,6 @@ +allowBuilds: + sharp: true + unrs-resolver: true +ignoredBuiltDependencies: + - sharp + - unrs-resolver diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 0000000..61e3684 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,7 @@ +const config = { + plugins: { + "@tailwindcss/postcss": {}, + }, +}; + +export default config; diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 0000000..004145c --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 0000000..567f17b --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 0000000..5174b28 --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 0000000..7705396 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 0000000..b2b2a44 --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/app/favicon.ico b/src/app/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..718d6fea4835ec2d246af9800eddb7ffb276240c GIT binary patch literal 25931 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 LrJugUA?W`A8`#=m literal 0 HcmV?d00001 diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..a2dc41e --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,26 @@ +@import "tailwindcss"; + +:root { + --background: #ffffff; + --foreground: #171717; +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +@media (prefers-color-scheme: dark) { + :root { + --background: #0a0a0a; + --foreground: #ededed; + } +} + +body { + background: var(--background); + color: var(--foreground); + font-family: Arial, Helvetica, sans-serif; +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..976eb90 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,33 @@ +import type { Metadata } from "next"; +import { Geist, Geist_Mono } from "next/font/google"; +import "./globals.css"; + +const geistSans = Geist({ + variable: "--font-geist-sans", + subsets: ["latin"], +}); + +const geistMono = Geist_Mono({ + variable: "--font-geist-mono", + subsets: ["latin"], +}); + +export const metadata: Metadata = { + title: "Create Next App", + description: "Generated by create next app", +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + {children} + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 0000000..3f36f7c --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,65 @@ +import Image from "next/image"; + +export default function Home() { + return ( +
+
+ Next.js logo +
+

+ To get started, edit the page.tsx file. +

+

+ Looking for a starting point or more instructions? Head over to{" "} + + Templates + {" "} + or the{" "} + + Learning + {" "} + center. +

+
+ +
+
+ ); +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..cf9c65d --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,34 @@ +{ + "compilerOptions": { + "target": "ES2017", + "lib": ["dom", "dom.iterable", "esnext"], + "allowJs": true, + "skipLibCheck": true, + "strict": true, + "noEmit": true, + "esModuleInterop": true, + "module": "esnext", + "moduleResolution": "bundler", + "resolveJsonModule": true, + "isolatedModules": true, + "jsx": "react-jsx", + "incremental": true, + "plugins": [ + { + "name": "next" + } + ], + "paths": { + "@/*": ["./src/*"] + } + }, + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx", + ".next/types/**/*.ts", + ".next/dev/types/**/*.ts", + "**/*.mts" + ], + "exclude": ["node_modules"] +} From 1f0ef78bba1dbb0a2061ed811bca678a9784790f Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 13 May 2026 13:59:59 +0900 Subject: [PATCH 002/103] =?UTF-8?q?chore:=20=EB=B0=B0=ED=8F=AC=20=EB=B0=8F?= =?UTF-8?q?=20preview=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/feature_request.md | 19 ++++++++++ .github/PULL_REQUEST_TEMPLATE.md | 45 +++++++++++++++++++++++ .github/workflows/deploy.yaml | 32 ++++++++++++++++ .github/workflows/preview.yaml | 43 ++++++++++++++++++++++ .gitignore | 1 + build.sh | 5 +++ 6 files changed, 145 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/deploy.yaml create mode 100644 .github/workflows/preview.yaml create mode 100644 build.sh diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..af1932e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,19 @@ +## 📌 작업 내용 + + + +- + +## ✅ 할 일 + + + +- [ ] +- [ ] +- [ ] + +## 🎨 참고 사항 + + + +- diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..6b8e297 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,45 @@ +## 📌 작업 내용 + + + +- + +## ✅ 변경 사항 + +- [ ] 새로운 기능 추가 +- [ ] 버그 수정 +- [ ] 코드 개선 및 수정 +- [ ] 문서 작성 및 업데이트 +- [ ] 배포 관련 수정 +- [ ] 브랜치 관련 수정 +- [ ] 기타: + +## 🔗 관련 이슈 + + + + +Closes # + +## 🧪 체크리스트 + +- [ ] 제가 작성한 코드를 리뷰했습니다. +- [ ] 문서에 변경 사항을 반영했습니다. +- [ ] 새로운 경고를 생성하지 않습니다. +- [ ] 새로운 기능이나 수정 사항이 기존 테스트와 충돌하지 않음을 확인했습니다. + +## 📸 스크린샷 + + + +없음 + +## 💬 리뷰 요청 사항 + + + +- + +## 기타 + +- diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml new file mode 100644 index 0000000..7a64923 --- /dev/null +++ b/.github/workflows/deploy.yaml @@ -0,0 +1,32 @@ +name: Deploy + +on: + push: + branches: ["master"] + +jobs: + build: + runs-on: ubuntu-latest + + container: pandoc/latex + + steps: + - uses: actions/checkout@v2 + - name: Install mustache (to update the date) + run: apk add ruby && gem install mustache + - name: creates output + run: sh ./build.sh + - name: Pushes to another repository + id: push_directory + uses: cpina/github-action-push-to-another-repository@main + env: + API_TOKEN_GITHUB: ${{ secrets.AUTO_ACTIONS }} + with: + source-directory: "output" + destination-github-username: GirimNam + destination-repository-name: next-vote-23rd + user-email: ${{ secrets.EMAIL }} + commit-message: ${{ github.event.commits[0].message }} + target-branch: master + - name: Test get variable exported by push-to-another-repository + run: echo $DESTINATION_CLONED_DIRECTORY diff --git a/.github/workflows/preview.yaml b/.github/workflows/preview.yaml new file mode 100644 index 0000000..0c98a5e --- /dev/null +++ b/.github/workflows/preview.yaml @@ -0,0 +1,43 @@ +name: Preview + +on: + pull_request: + branches: ["master"] + +jobs: + vercel-preview: + runs-on: ubuntu-latest + + env: + VERCEL_ORG_ID: ${{ secrets.VERCEL_ORG_ID }} + VERCEL_PROJECT_ID: ${{ secrets.VERCEL_PROJECT_ID }} + + steps: + - uses: actions/checkout@v4 + - name: Install Vercel CLI + run: npm install --global vercel@latest && npm install --global pnpm + - name: Get Vercel Environment Variables + run: vercel pull --yes --environment=preview --token=${{ secrets.VERCEL_TOKEN }} + - name: Build Project Artifacts + run: vercel build --token=${{ secrets.VERCEL_TOKEN }} + - name: Deploy Project Artifacts to Vercel + id: deploy + + run: | + + vercel deploy --prebuilt --token=${{ secrets.VERCEL_TOKEN }} > vercel-output.txt + echo "preview_url=$(cat vercel-output.txt)" >> $GITHUB_OUTPUT + + - name: Comment PR with Preview URL + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + ✅ PREVIEW ${{ steps.deploy.outputs.preview_url }} + +permissions: + contents: read + pages: write + deployments: write + id-token: write + issues: write + pull-requests: write diff --git a/.gitignore b/.gitignore index 5ef6a52..e3a7542 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ yarn-error.log* # typescript *.tsbuildinfo next-env.d.ts +.env*.local diff --git a/build.sh b/build.sh new file mode 100644 index 0000000..0251617 --- /dev/null +++ b/build.sh @@ -0,0 +1,5 @@ +#!/bin/sh +cd ../ +mkdir output +cp -R ./next-vote-23rd/* ./output +cp -R ./output ./next-vote-23rd/ \ No newline at end of file From 4ba00554d20bea1fc68478ff9e250dc8736fe9c0 Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 13 May 2026 14:04:30 +0900 Subject: [PATCH 003/103] =?UTF-8?q?doc:=20=EB=B0=B0=ED=8F=AC=20=EB=AC=B8?= =?UTF-8?q?=EC=84=9C=20=EB=B2=84=EC=A0=84=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/deploy.yaml b/.github/workflows/deploy.yaml index 7a64923..91baf5c 100644 --- a/.github/workflows/deploy.yaml +++ b/.github/workflows/deploy.yaml @@ -11,7 +11,7 @@ jobs: container: pandoc/latex steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Install mustache (to update the date) run: apk add ruby && gem install mustache - name: creates output From 547e979dbde540347aa31540121f9b9301cdb2a2 Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 13 May 2026 14:18:07 +0900 Subject: [PATCH 004/103] =?UTF-8?q?doc:=20issue=20template=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/ISSUE_TEMPLATE/feature_request.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md index af1932e..0be826a 100644 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -1,3 +1,11 @@ +--- +name: 기능 요청 +about: 새로운 기능 추가 또는 개선 사항을 작성합니다. +title: "[FEAT] " +labels: enhancement +assignees: "" +--- + ## 📌 작업 내용 From 40718475f5402ee9335286ce16a3e0cd5eee9853 Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 13 May 2026 14:55:56 +0900 Subject: [PATCH 005/103] =?UTF-8?q?chore:=20=EB=B6=88=ED=95=84=EC=9A=94=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/file.svg | 1 - public/globe.svg | 1 - public/next.svg | 1 - public/vercel.svg | 1 - public/window.svg | 1 - src/app/favicon.ico | Bin 25931 -> 0 bytes 6 files changed, 5 deletions(-) delete mode 100644 public/file.svg delete mode 100644 public/globe.svg delete mode 100644 public/next.svg delete mode 100644 public/vercel.svg delete mode 100644 public/window.svg delete mode 100644 src/app/favicon.ico diff --git a/public/file.svg b/public/file.svg deleted file mode 100644 index 004145c..0000000 --- a/public/file.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/globe.svg b/public/globe.svg deleted file mode 100644 index 567f17b..0000000 --- a/public/globe.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/next.svg b/public/next.svg deleted file mode 100644 index 5174b28..0000000 --- a/public/next.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/vercel.svg b/public/vercel.svg deleted file mode 100644 index 7705396..0000000 --- a/public/vercel.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/public/window.svg b/public/window.svg deleted file mode 100644 index b2b2a44..0000000 --- a/public/window.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/src/app/favicon.ico b/src/app/favicon.ico deleted file mode 100644 index 718d6fea4835ec2d246af9800eddb7ffb276240c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25931 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 LrJugUA?W`A8`#=m From 348a0a397bce9fbd249089e20cab5f1448a37a27 Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 13 May 2026 15:31:52 +0900 Subject: [PATCH 006/103] =?UTF-8?q?feat:=20css=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/globals.css | 54 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 51 insertions(+), 3 deletions(-) diff --git a/src/app/globals.css b/src/app/globals.css index a2dc41e..9fe6bf9 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,3 +1,4 @@ +@import url("https://cdn.jsdelivr.net/gh/orioncactus/pretendard/dist/web/static/pretendard.css"); @import "tailwindcss"; :root { @@ -6,10 +7,57 @@ } @theme inline { + --font-sans: "Pretendard", sans-serif; --color-background: var(--background); --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --font-pretendard: "Pretendard", sans-serif; + + --color-skyblue: #5da9ff; + --color-blue: #1b7be8; + + /* Heading */ + --text-heading1: 24px; + --text-heading1--line-height: 135%; + --text-heading1--letter-spacing: -0.02em; + --text-heading1--font-weight: 600; + + --text-heading2: 20px; + --text-heading2--line-height: 135%; + --text-heading2--letter-spacing: -0.02em; + --text-heading2--font-weight: 600; + + /* Body */ + --text-body1: 18px; + --text-body1--line-height: 135%; + --text-body1--letter-spacing: -0.02em; + --text-body1--font-weight: 400; + + --text-body2: 16px; + --text-body2--line-height: 135%; + --text-body2--letter-spacing: -0.02em; + --text-body2--font-weight: 400; + + /* Label */ + --text-label1: 20px; + --text-label1--line-height: 135%; + --text-label1--letter-spacing: -0.02em; + --text-label1--font-weight: 600; + + --text-label2: 15px; + --text-label2--line-height: 135%; + --text-label2--letter-spacing: -0.02em; + --text-label2--font-weight: 400; + + /* Caption */ + --text-caption1: 12px; + --text-caption1--line-height: 135%; + --text-caption1--letter-spacing: -0.02em; + --text-caption1--font-weight: 400; + + --text-caption2: 10px; + --text-caption2--line-height: 135%; + --text-caption2--letter-spacing: -0.02em; + --text-caption2--font-weight: 500; } @media (prefers-color-scheme: dark) { @@ -22,5 +70,5 @@ body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: "Pretendard", Arial, Helvetica, sans-serif; } From 3dbf1e1ee196fa6371efd0f173ae67336ab0304d Mon Sep 17 00:00:00 2001 From: girimNam Date: Thu, 14 May 2026 01:23:45 +0900 Subject: [PATCH 007/103] =?UTF-8?q?feat:=20=ED=97=A4=EB=8D=94=20=EB=84=A4?= =?UTF-8?q?=EB=B9=84=EA=B2=8C=EC=9D=B4=EC=85=98=20=EC=A0=9C=EC=99=B8=20?= =?UTF-8?q?=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/login/page.tsx | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/app/(auth)/login/page.tsx diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx new file mode 100644 index 0000000..c4ca9d4 --- /dev/null +++ b/src/app/(auth)/login/page.tsx @@ -0,0 +1,29 @@ +import Link from "next/link"; + +export default function Login() { + return ( + <> + {/* 헤더 -> 네비게이션 자리 */} + +
+

LOGIN

+ + + + +
+ + ); +} From 96db55cdfe64489df46ddcfdf79b87d6f10f5bef Mon Sep 17 00:00:00 2001 From: girimNam Date: Thu, 14 May 2026 11:19:13 +0900 Subject: [PATCH 008/103] =?UTF-8?q?feat:=20auth=20layout=20=EC=84=A4?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/layout.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/app/(auth)/layout.tsx diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx new file mode 100644 index 0000000..f6ec520 --- /dev/null +++ b/src/app/(auth)/layout.tsx @@ -0,0 +1,11 @@ +export default function AuthLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ {children} +
+ ); +} From cd22b158f4dff2f9c678343ada6291dfb68f2142 Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 11:34:19 +0900 Subject: [PATCH 009/103] =?UTF-8?q?feat:=20=ED=8C=80/=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/constants/teams.ts | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 src/constants/teams.ts diff --git a/src/constants/teams.ts b/src/constants/teams.ts new file mode 100644 index 0000000..99f06c5 --- /dev/null +++ b/src/constants/teams.ts @@ -0,0 +1,28 @@ +export type Part = "frontend" | "backend"; + +export type TeamName = "Ditda" | "JobDri" | "Groupeat" | "IPX" | "CONX"; + +export const TEAM_NAMES: TeamName[] = [ + "Ditda", + "JobDri", + "Groupeat", + "IPX", + "CONX", +]; + +export const TEAM_MEMBERS: Record> = { + frontend: { + Ditda: ["박유민", "권오진"], + JobDri: ["이윤서", "구민교"], + Groupeat: ["이승연", "황영준"], + IPX: ["남기림", "김민서"], + CONX: ["김홍엽", "오유진"], + }, + backend: { + Ditda: ["임종훈", "안준석"], + JobDri: ["황신애", "최우혁"], + Groupeat: ["김동욱", "최승원"], + IPX: ["오지송", "김태익"], + CONX: ["김태희", "김도현"], + }, +}; From 17778de0b063694b3a56c829a5fc5bdcdcab0842 Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 11:44:13 +0900 Subject: [PATCH 010/103] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 src/app/(auth)/signup/page.tsx diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx new file mode 100644 index 0000000..d49c379 --- /dev/null +++ b/src/app/(auth)/signup/page.tsx @@ -0,0 +1,9 @@ +export default function Signup() { + return ( +
+

+ SIGNUP +

+
+ ); +} From d3756d7c4fe53c7cb7b943b0deb17cb1c458b2e0 Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 11:49:16 +0900 Subject: [PATCH 011/103] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=8C=8C=ED=8A=B8=20=EC=84=A0=ED=83=9D=20=ED=83=AD?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index d49c379..856e9ff 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -1,9 +1,35 @@ +"use client"; + +import { useState } from "react"; +import { Part } from "@/constants/teams"; + export default function Signup() { + const [part, setPart] = useState("frontend"); + return ( -
+

SIGNUP

+ +
+ {(["frontend", "backend"] as const).map((p, idx) => { + const selected = part === p; + const rounded = idx === 0 ? "rounded-l-[12px]" : "rounded-r-[12px]"; + return ( + + ); + })} +
); } From 076a2c6e06c0daf7289e9f41db0a9ab3d4bf2137 Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 11:57:18 +0900 Subject: [PATCH 012/103] =?UTF-8?q?feat:=20=ED=8C=80/=EC=9D=B4=EB=A6=84=20?= =?UTF-8?q?=EB=93=9C=EB=A1=AD=EB=8B=A4=EC=9A=B4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 112 ++++++++++++++++++++++++++++++++- 1 file changed, 109 insertions(+), 3 deletions(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 856e9ff..0de227b 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -1,10 +1,98 @@ "use client"; -import { useState } from "react"; -import { Part } from "@/constants/teams"; +import { useEffect, useRef, useState } from "react"; +import { + Part, + TEAM_MEMBERS, + TEAM_NAMES, + TeamName, +} from "@/constants/teams"; + +type DropdownProps = { + label: string; + value: string; + placeholder: string; + options: string[]; + onChange: (value: string) => void; + disabled?: boolean; +}; + +function Dropdown({ + label, + value, + placeholder, + options, + onChange, + disabled, +}: DropdownProps) { + const [open, setOpen] = useState(false); + const ref = useRef(null); + + useEffect(() => { + if (!open) return; + const handleClick = (e: MouseEvent) => { + if (!ref.current?.contains(e.target as Node)) setOpen(false); + }; + document.addEventListener("mousedown", handleClick); + return () => document.removeEventListener("mousedown", handleClick); + }, [open]); + + return ( +
+ {label} +
+ + {open && options.length > 0 && ( +
    + {options.map((opt) => ( +
  • + +
  • + ))} +
+ )} +
+
+ ); +} export default function Signup() { const [part, setPart] = useState("frontend"); + const [team, setTeam] = useState(""); + const [name, setName] = useState(""); + + const memberOptions = team ? TEAM_MEMBERS[part][team] : []; + + const handlePartChange = (next: Part) => { + if (next === part) return; + setPart(next); + setTeam(""); + setName(""); + }; + + const handleTeamChange = (next: string) => { + setTeam(next as TeamName); + setName(""); + }; return (
@@ -20,7 +108,7 @@ export default function Signup() {
); } From 754828c614eff429c2e32223141cf45c2d737367 Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 11:59:41 +0900 Subject: [PATCH 013/103] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=9E=85=EB=A0=A5=20=ED=95=84=EB=93=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 94 +++++++++++++++++++++++++++------- 1 file changed, 75 insertions(+), 19 deletions(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 0de227b..6579a3a 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -79,9 +79,18 @@ export default function Signup() { const [part, setPart] = useState("frontend"); const [team, setTeam] = useState(""); const [name, setName] = useState(""); + const [username, setUsername] = useState(""); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + const [passwordRe, setPasswordRe] = useState(""); const memberOptions = team ? TEAM_MEMBERS[part][team] : []; + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + // TODO: 회원가입 API 연동 + }; + const handlePartChange = (next: Part) => { if (next === part) return; setPart(next); @@ -95,8 +104,8 @@ export default function Signup() { }; return ( -
-

+

+

SIGNUP

@@ -119,23 +128,70 @@ export default function Signup() { })} -
- - -
+
+
+ + +
+ + + + + + + + + +
); } From 2a8da5bb59d683cab91a02700851544d551295bf Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 12:14:07 +0900 Subject: [PATCH 014/103] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=ED=95=98=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 6579a3a..e2a2566 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -191,6 +191,12 @@ export default function Signup() { /> +
); From cb60fd98c3b0341cec694b67cdfb1a4a6457eb50 Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 12:16:37 +0900 Subject: [PATCH 015/103] =?UTF-8?q?feat:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=A6=9D=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index e2a2566..b247cf2 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -86,6 +86,10 @@ export default function Signup() { const memberOptions = team ? TEAM_MEMBERS[part][team] : []; + const EMAIL_PATTERN = /^[A-Za-z0-9_.-]+@[A-Za-z0-9-]+\.[A-Za-z0-9-]+/; + const isEmailValid = EMAIL_PATTERN.test(email); + const showEmailError = email.length > 0 && !isEmailValid; + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); // TODO: 회원가입 API 연동 @@ -158,16 +162,23 @@ export default function Signup() { /> - +
+ + {showEmailError && ( +

+ 이메일 형식이 올바르지 않습니다 +

+ )} +
- +
+ + {showPasswordError && ( +

+ 비밀번호가 일치하지 않습니다 +

+ )} +
From 8559a0ed2ce357013530a6e8c8b90637e43f86c6 Mon Sep 17 00:00:00 2001 From: girimNam Date: Thu, 14 May 2026 15:47:42 +0900 Subject: [PATCH 018/103] =?UTF-8?q?chore:=20pnpm=20=EB=B2=84=EC=A0=84=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 5 ++++- pnpm-lock.yaml | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 38 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index e422c70..02fea21 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,12 @@ "lint": "eslint" }, "dependencies": { + "@hookform/resolvers": "^5.2.2", "next": "16.2.6", "react": "19.2.4", - "react-dom": "19.2.4" + "react-dom": "19.2.4", + "react-hook-form": "^7.75.0", + "zod": "^4.4.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7569832..5adec37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@hookform/resolvers': + specifier: ^5.2.2 + version: 5.2.2(react-hook-form@7.75.0(react@19.2.4)) next: specifier: 16.2.6 version: 16.2.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -17,6 +20,12 @@ importers: react-dom: specifier: 19.2.4 version: 19.2.4(react@19.2.4) + react-hook-form: + specifier: ^7.75.0 + version: 7.75.0(react@19.2.4) + zod: + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@tailwindcss/postcss': specifier: ^4 @@ -166,6 +175,11 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@hookform/resolvers@5.2.2': + resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} + peerDependencies: + react-hook-form: ^7.55.0 + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -435,6 +449,9 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -1680,6 +1697,12 @@ packages: peerDependencies: react: ^19.2.4 + react-hook-form@7.75.0: + resolution: {integrity: sha512-Ovv94H+0p3sJ7B9B5QxPuCP1u8V/cHuVGyH55cSwodYDtoJwK+fqk3vjfIgSX59I2U/bU4z0nRJ9HMLpNiWEmw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2126,6 +2149,11 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@hookform/resolvers@5.2.2(react-hook-form@7.75.0(react@19.2.4))': + dependencies: + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.75.0(react@19.2.4) + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -2311,6 +2339,8 @@ snapshots: '@rtsao/scc@1.1.0': {} + '@standard-schema/utils@0.3.0': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -3673,6 +3703,10 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 + react-hook-form@7.75.0(react@19.2.4): + dependencies: + react: 19.2.4 + react-is@16.13.1: {} react@19.2.4: {} From 8c1dc0257e49ca90fd2b1c058552d1343e2bb1f4 Mon Sep 17 00:00:00 2001 From: girimNam Date: Thu, 14 May 2026 15:47:52 +0900 Subject: [PATCH 019/103] =?UTF-8?q?feat:=20NavBar=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/layout.tsx | 11 +++++++-- src/app/(auth)/login/page.tsx | 43 ++++++++++++++++------------------- src/components/NavBar.tsx | 39 +++++++++++++++++++++++++++++++ 3 files changed, 68 insertions(+), 25 deletions(-) create mode 100644 src/components/NavBar.tsx diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx index f6ec520..1ebfc41 100644 --- a/src/app/(auth)/layout.tsx +++ b/src/app/(auth)/layout.tsx @@ -1,11 +1,18 @@ +import NavBar from "@/components/NavBar"; + export default function AuthLayout({ children, }: { children: React.ReactNode; }) { return ( -
- {children} +
+
+ +
+
+ {children} +
); } diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index c4ca9d4..0a3cf11 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -1,29 +1,26 @@ import Link from "next/link"; +import z from "zod"; export default function Login() { return ( - <> - {/* 헤더 -> 네비게이션 자리 */} - -
-

LOGIN

- - - - -
- +
+

LOGIN

+ + + + +
); } diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx new file mode 100644 index 0000000..f886eb2 --- /dev/null +++ b/src/components/NavBar.tsx @@ -0,0 +1,39 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; + +interface NavItem { + label: string; + href: string; +} + +const NAV_ITEMS: NavItem[] = [ + { label: "VOTING", href: "#" }, + { label: "MEMBERS", href: "#" }, + { label: "ABOUT US", href: "#" }, + { label: "LOGIN", href: "/login" }, +]; + +export default function NavBar() { + const pathname = usePathname(); + + return ( +
+
+ 2026 23TH CEOS AWARDS +
+
+ {NAV_ITEMS.map((item) => ( + + {item.label} + + ))} +
+
+ ); +} From 9be6093c01ea36c1295d3677a59bd84625d45f19 Mon Sep 17 00:00:00 2001 From: minseo Date: Thu, 14 May 2026 15:52:46 +0900 Subject: [PATCH 020/103] =?UTF-8?q?feat:=20react-hook-form=20+=20zod=20?= =?UTF-8?q?=EB=8F=84=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package.json | 5 +- pnpm-lock.yaml | 34 +++++++ src/app/(auth)/signup/page.tsx | 158 +++++++++++++++++++-------------- src/schemas/signup.ts | 20 +++++ 4 files changed, 151 insertions(+), 66 deletions(-) create mode 100644 src/schemas/signup.ts diff --git a/package.json b/package.json index e422c70..02fea21 100644 --- a/package.json +++ b/package.json @@ -9,9 +9,12 @@ "lint": "eslint" }, "dependencies": { + "@hookform/resolvers": "^5.2.2", "next": "16.2.6", "react": "19.2.4", - "react-dom": "19.2.4" + "react-dom": "19.2.4", + "react-hook-form": "^7.75.0", + "zod": "^4.4.3" }, "devDependencies": { "@tailwindcss/postcss": "^4", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7569832..5adec37 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,6 +8,9 @@ importers: .: dependencies: + '@hookform/resolvers': + specifier: ^5.2.2 + version: 5.2.2(react-hook-form@7.75.0(react@19.2.4)) next: specifier: 16.2.6 version: 16.2.6(@babel/core@7.29.0)(babel-plugin-react-compiler@1.0.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4) @@ -17,6 +20,12 @@ importers: react-dom: specifier: 19.2.4 version: 19.2.4(react@19.2.4) + react-hook-form: + specifier: ^7.75.0 + version: 7.75.0(react@19.2.4) + zod: + specifier: ^4.4.3 + version: 4.4.3 devDependencies: '@tailwindcss/postcss': specifier: ^4 @@ -166,6 +175,11 @@ packages: resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + '@hookform/resolvers@5.2.2': + resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==} + peerDependencies: + react-hook-form: ^7.55.0 + '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} @@ -435,6 +449,9 @@ packages: '@rtsao/scc@1.1.0': resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + '@standard-schema/utils@0.3.0': + resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} + '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} @@ -1680,6 +1697,12 @@ packages: peerDependencies: react: ^19.2.4 + react-hook-form@7.75.0: + resolution: {integrity: sha512-Ovv94H+0p3sJ7B9B5QxPuCP1u8V/cHuVGyH55cSwodYDtoJwK+fqk3vjfIgSX59I2U/bU4z0nRJ9HMLpNiWEmw==} + engines: {node: '>=18.0.0'} + peerDependencies: + react: ^16.8.0 || ^17 || ^18 || ^19 + react-is@16.13.1: resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} @@ -2126,6 +2149,11 @@ snapshots: '@eslint/core': 0.17.0 levn: 0.4.1 + '@hookform/resolvers@5.2.2(react-hook-form@7.75.0(react@19.2.4))': + dependencies: + '@standard-schema/utils': 0.3.0 + react-hook-form: 7.75.0(react@19.2.4) + '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 @@ -2311,6 +2339,8 @@ snapshots: '@rtsao/scc@1.1.0': {} + '@standard-schema/utils@0.3.0': {} + '@swc/helpers@0.5.15': dependencies: tslib: 2.8.1 @@ -3673,6 +3703,10 @@ snapshots: react: 19.2.4 scheduler: 0.27.0 + react-hook-form@7.75.0(react@19.2.4): + dependencies: + react: 19.2.4 + react-is@16.13.1: {} react@19.2.4: {} diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 8b78684..08ca673 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -1,12 +1,10 @@ "use client"; import { useEffect, useRef, useState } from "react"; -import { - Part, - TEAM_MEMBERS, - TEAM_NAMES, - TeamName, -} from "@/constants/teams"; +import { Controller, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { TEAM_MEMBERS, TEAM_NAMES } from "@/constants/teams"; +import { signupSchema, SignupForm } from "@/schemas/signup"; type DropdownProps = { label: string; @@ -76,38 +74,47 @@ function Dropdown({ } export default function Signup() { - const [part, setPart] = useState("frontend"); - const [team, setTeam] = useState(""); - const [name, setName] = useState(""); - const [username, setUsername] = useState(""); - const [email, setEmail] = useState(""); - const [password, setPassword] = useState(""); - const [passwordRe, setPasswordRe] = useState(""); - - const memberOptions = team ? TEAM_MEMBERS[part][team] : []; - - const EMAIL_PATTERN = /^[A-Za-z0-9_.-]+@[A-Za-z0-9-]+\.[A-Za-z0-9-]+/; - const isEmailValid = EMAIL_PATTERN.test(email); - const showEmailError = email.length > 0 && !isEmailValid; - - const isPasswordMatched = password === passwordRe; - const showPasswordError = passwordRe.length > 0 && !isPasswordMatched; - - const handleSubmit = (e: React.FormEvent) => { - e.preventDefault(); - // TODO: 회원가입 API 연동 - }; - - const handlePartChange = (next: Part) => { + const { + register, + control, + handleSubmit, + watch, + setValue, + formState: { errors }, + } = useForm({ + resolver: zodResolver(signupSchema), + mode: "onChange", + defaultValues: { + part: "frontend", + team: "", + member: "", + username: "", + email: "", + password: "", + passwordRe: "", + }, + }); + + const part = watch("part"); + const team = watch("team"); + const email = watch("email"); + const passwordRe = watch("passwordRe"); + + const memberOptions = + team && TEAM_MEMBERS[part][team as keyof (typeof TEAM_MEMBERS)["frontend"]] + ? TEAM_MEMBERS[part][team as keyof (typeof TEAM_MEMBERS)["frontend"]] + : []; + + const handlePartChange = (next: SignupForm["part"]) => { if (next === part) return; - setPart(next); - setTeam(""); - setName(""); + setValue("part", next); + setValue("team", ""); + setValue("member", ""); }; - const handleTeamChange = (next: string) => { - setTeam(next as TeamName); - setName(""); + const onSubmit = (data: SignupForm) => { + console.log(data); + // TODO: 회원가입 API 연동 }; return ( @@ -135,75 +142,96 @@ export default function Signup() { })}
- +
- ( + { + field.onChange(v); + setValue("member", ""); + }} + /> + )} /> - ( + + )} />
-

- {showEmailError ? "이메일 형식이 올바르지 않습니다" : " "} +

+ {email.length > 0 && errors.email ? errors.email.message : " "}

-

- {showPasswordError ? "비밀번호가 일치하지 않습니다" : " "} +

+ {passwordRe.length > 0 && errors.passwordRe + ? errors.passwordRe.message + : " "}

- + + + 아직 계정이 없나요? 회원 가입 하러 가기 - +
); } diff --git a/src/schemas/login.ts b/src/schemas/login.ts new file mode 100644 index 0000000..6ad6023 --- /dev/null +++ b/src/schemas/login.ts @@ -0,0 +1,8 @@ +import { z } from "zod"; + +export const loginSchema = z.object({ + username: z.string().min(1, "아이디를 입력해 주세요"), + password: z.string().min(1, "비밀번호를 입력해 주세요"), +}); + +export type LoginForm = z.infer; From c8ec2a9ea67d0a690b6e5dcce27391f8f71fd07f Mon Sep 17 00:00:00 2001 From: girimNam Date: Fri, 15 May 2026 19:53:27 +0900 Subject: [PATCH 024/103] =?UTF-8?q?chore:=20label=20=ED=83=9C=EA=B7=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/login/page.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index bacde47..d58a12f 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -33,7 +33,7 @@ export default function Login() {

LOGIN

-
+
+ -
+
+ - + + {/* 모바일 드로어 */} + {open && ( +
+
setOpen(false)} /> +
+ + {NAV_ITEMS.map((item) => ( + setOpen(false)} + className={`text-2xl font-bold ${pathname === item.href ? "text-blue-400" : "text-white"}`} + > + {item.label} + + ))} +
+
+ )} + ); } From f94073f13b3d5734a42252f0b2ca73613e6ce314 Mon Sep 17 00:00:00 2001 From: minseo Date: Sat, 16 May 2026 11:49:59 +0900 Subject: [PATCH 027/103] =?UTF-8?q?feat:=20=ED=8C=8C=ED=8A=B8=EC=9E=A5=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/leader/page.tsx | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/app/voting/leader/page.tsx diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx new file mode 100644 index 0000000..b4861ea --- /dev/null +++ b/src/app/voting/leader/page.tsx @@ -0,0 +1,3 @@ +export default function VotingLeader() { + return

파트장 투표

; +} From 697d707d4fe08cd856fe156890be79d20eac58db Mon Sep 17 00:00:00 2001 From: minseo Date: Sat, 16 May 2026 11:50:27 +0900 Subject: [PATCH 028/103] =?UTF-8?q?feat:=20=EB=8D=B0=EB=AA=A8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=20=ED=88=AC=ED=91=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/demoday/page.tsx | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/app/voting/demoday/page.tsx diff --git a/src/app/voting/demoday/page.tsx b/src/app/voting/demoday/page.tsx new file mode 100644 index 0000000..acd8ce2 --- /dev/null +++ b/src/app/voting/demoday/page.tsx @@ -0,0 +1,3 @@ +export default function VotingDemoday() { + return

데모데이 투표

; +} From f4ff9e19b701f56c5277197b4bb013af38ae0939 Mon Sep 17 00:00:00 2001 From: minseo Date: Sat, 16 May 2026 11:56:32 +0900 Subject: [PATCH 029/103] =?UTF-8?q?feat:=20MEMBERS=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/members/page.tsx | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 src/app/members/page.tsx diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx new file mode 100644 index 0000000..154ed4d --- /dev/null +++ b/src/app/members/page.tsx @@ -0,0 +1,3 @@ +export default function Members() { + return

MEMBERS

; +} From 90c41744f6ec185e92e59b9203eca0afcb9cc7ee Mon Sep 17 00:00:00 2001 From: minseo Date: Sat, 16 May 2026 13:02:53 +0900 Subject: [PATCH 030/103] =?UTF-8?q?refactor:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=8B=A8=EC=9C=84=20?= =?UTF-8?q?rem=EC=9C=BC=EB=A1=9C=20=ED=99=98=EC=82=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 08ca673..01e1838 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -118,21 +118,21 @@ export default function Signup() { }; return ( -
-

+

+

SIGNUP

{(["frontend", "backend"] as const).map((p, idx) => { const selected = part === p; - const rounded = idx === 0 ? "rounded-l-[12px]" : "rounded-r-[12px]"; + const rounded = idx === 0 ? "rounded-l-[0.75rem]" : "rounded-r-[0.75rem]"; return (
); } From bf970999dd1f9fa8bd10da099436b89af86fa30d Mon Sep 17 00:00:00 2001 From: minseo Date: Sat, 16 May 2026 20:55:15 +0900 Subject: [PATCH 044/103] =?UTF-8?q?style:=20=EB=8D=B0=EB=AA=A8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B0=98=EC=9D=91?= =?UTF-8?q?=ED=98=95=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/demoday/page.tsx | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/app/voting/demoday/page.tsx b/src/app/voting/demoday/page.tsx index 7cc52ca..7cb32b8 100644 --- a/src/app/voting/demoday/page.tsx +++ b/src/app/voting/demoday/page.tsx @@ -7,11 +7,10 @@ export default function VotingDemoday() { const [selected, setSelected] = useState(null); return ( -
-
+
+
투표하기 >}
-
    +
      {TEAM_NAMES.map((team) => (
    • From ddbdb4a99d31c39780cb11aab4a7e0acb09117dc Mon Sep 17 00:00:00 2001 From: minseo Date: Sat, 16 May 2026 20:59:23 +0900 Subject: [PATCH 045/103] =?UTF-8?q?style:=20=ED=8C=8C=ED=8A=B8=EC=9E=A5=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=B0=98=EC=9D=91=ED=98=95=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/leader/page.tsx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx index 79d761f..a5752f5 100644 --- a/src/app/voting/leader/page.tsx +++ b/src/app/voting/leader/page.tsx @@ -16,20 +16,20 @@ export default function VotingLeader() { return (
      -

      +

      FE - LEADER

      -
        +
          {LEADER_CANDIDATES.map((name) => (
        • @@ -40,7 +40,7 @@ export default function VotingLeader() { + ) : ( + + LOGIN + + )}
@@ -78,11 +100,30 @@ export default function NavBar({ className }: { className?: string }) { key={item.label} href={item.href} onClick={() => setOpen(false)} - className={`text-2xl font-bold ${pathname === item.href ? "text-blue-400" : "text-white"}`} + className={`text-2xl font-bold ${pathname === item.href ? "text-blue-500" : "text-white"}`} > {item.label} ))} + {isLoggedIn ? ( + + ) : ( + setOpen(false)} + className={`text-2xl font-bold ${pathname === "/login" ? "text-blue-500" : "text-white"}`} + > + LOGIN + + )} )} diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx new file mode 100644 index 0000000..f9c6251 --- /dev/null +++ b/src/contexts/AuthContext.tsx @@ -0,0 +1,38 @@ +"use client"; + +import { createContext, useContext, useState } from "react"; +import { setAccessToken, clearAccessToken } from "@/lib/auth"; + +interface AuthContextType { + isLoggedIn: boolean; + login: (token: string) => void; + logout: () => void; +} + +const AuthContext = createContext(null); + +export function AuthProvider({ children }: { children: React.ReactNode }) { + const [isLoggedIn, setIsLoggedIn] = useState(false); + + const login = (token: string) => { + setAccessToken(token); + setIsLoggedIn(true); + }; + + const logout = () => { + clearAccessToken(); + setIsLoggedIn(false); + }; + + return ( + + {children} + + ); +} + +export function useAuth() { + const ctx = useContext(AuthContext); + if (!ctx) throw new Error("useAuth must be used within AuthProvider"); + return ctx; +} From 43a35bf98ac42718b133a5c8ccab132c002597e8 Mon Sep 17 00:00:00 2001 From: girimNam Date: Sat, 16 May 2026 23:09:27 +0900 Subject: [PATCH 047/103] =?UTF-8?q?feat:=20about=20us=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=83=9D=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/figures/figure-about-big.svg | 27 +++++++++++++++++++++++++++ public/figures/figure-about-small.svg | 15 +++++++++++++++ src/app/about/layout.tsx | 14 ++++++++++++++ src/app/about/page.tsx | 22 ++++++++++++++++++++++ src/components/NavBar.tsx | 2 +- 5 files changed, 79 insertions(+), 1 deletion(-) create mode 100644 public/figures/figure-about-big.svg create mode 100644 public/figures/figure-about-small.svg create mode 100644 src/app/about/layout.tsx create mode 100644 src/app/about/page.tsx diff --git a/public/figures/figure-about-big.svg b/public/figures/figure-about-big.svg new file mode 100644 index 0000000..776dd43 --- /dev/null +++ b/public/figures/figure-about-big.svg @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/figures/figure-about-small.svg b/public/figures/figure-about-small.svg new file mode 100644 index 0000000..1d93423 --- /dev/null +++ b/public/figures/figure-about-small.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/src/app/about/layout.tsx b/src/app/about/layout.tsx new file mode 100644 index 0000000..b6b7def --- /dev/null +++ b/src/app/about/layout.tsx @@ -0,0 +1,14 @@ +import NavBar from "@/components/NavBar"; + +export default function MembersLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( +
+ +
{children}
+
+ ); +} diff --git a/src/app/about/page.tsx b/src/app/about/page.tsx new file mode 100644 index 0000000..bc6c6c9 --- /dev/null +++ b/src/app/about/page.tsx @@ -0,0 +1,22 @@ +import Image from "next/image"; + +export default function About() { + return ( +
+ About CEOS + About CEOS +
+ ); +} diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 4bc09f9..330cd63 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -14,7 +14,7 @@ interface NavItem { const NAV_ITEMS: NavItem[] = [ { label: "VOTING", href: "#" }, { label: "MEMBERS", href: "/members" }, - { label: "ABOUT US", href: "#" }, + { label: "ABOUT US", href: "/about" }, ]; export default function NavBar({ className }: { className?: string }) { From 767d705d55ee2255acf1f65e8a495c9a9ab2af8c Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 00:22:34 +0900 Subject: [PATCH 048/103] =?UTF-8?q?style:=20FE=20=ED=8C=8C=ED=8A=B8?= =?UTF-8?q?=EC=9E=A5=20=ED=88=AC=ED=91=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20?= =?UTF-8?q?=ED=83=80=EC=9D=B4=ED=8B=80=20=EB=B0=8F=20=ED=88=AC=ED=91=9C=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20UI=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/Ellipse 15.svg | 17 +++++++++++++ public/Ellipse 8.svg | 9 +++++++ public/Star 8.svg | 3 +++ src/app/voting/leader/page.tsx | 45 ++++++++++++++++++++++++++++------ 4 files changed, 66 insertions(+), 8 deletions(-) create mode 100644 public/Ellipse 15.svg create mode 100644 public/Ellipse 8.svg create mode 100644 public/Star 8.svg diff --git a/public/Ellipse 15.svg b/public/Ellipse 15.svg new file mode 100644 index 0000000..783c7b6 --- /dev/null +++ b/public/Ellipse 15.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/Ellipse 8.svg b/public/Ellipse 8.svg new file mode 100644 index 0000000..c5edc9b --- /dev/null +++ b/public/Ellipse 8.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/Star 8.svg b/public/Star 8.svg new file mode 100644 index 0000000..18f90ac --- /dev/null +++ b/public/Star 8.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx index a5752f5..332df51 100644 --- a/src/app/voting/leader/page.tsx +++ b/src/app/voting/leader/page.tsx @@ -15,12 +15,37 @@ export default function VotingLeader() { const [selected, setSelected] = useState(null); return ( -
-

- FE - LEADER -

+
setSelected(null)} + className="relative min-h-screen bg-gradient-to-b from-[#FFFFFF] via-[#D2E6FD] to-[#FFFFFF]" + > +
+
+ + + +

+ FE - LEADER +

+
+
-
    +
      {LEADER_CANDIDATES.map((name) => (
); From 138267dde15d4a00ee7091fe5134984d199df428 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 00:27:17 +0900 Subject: [PATCH 049/103] =?UTF-8?q?feat:=20=ED=8C=8C=ED=8A=B8=EC=9E=A5=20?= =?UTF-8?q?=ED=88=AC=ED=91=9C=20=EC=99=B8=EB=B6=80=20=ED=81=B4=EB=A6=AD=20?= =?UTF-8?q?=EC=8B=9C=20=EC=84=A0=ED=83=9D=20=ED=95=B4=EC=A0=9C=20=EB=B0=8F?= =?UTF-8?q?=20=EC=84=A0=ED=83=9D=20=ED=9A=A8=EA=B3=BC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/leader/page.tsx | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx index 332df51..e95a057 100644 --- a/src/app/voting/leader/page.tsx +++ b/src/app/voting/leader/page.tsx @@ -50,11 +50,22 @@ export default function VotingLeader() {
  • From edc0baaf39b9d266e8f28f6f5d89feb66afd4155 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 00:51:37 +0900 Subject: [PATCH 050/103] =?UTF-8?q?refactor:=20=ED=8C=8C=ED=8A=B8=EC=9E=A5?= =?UTF-8?q?=20=ED=88=AC=ED=91=9C=20=ED=8E=98=EC=9D=B4=EC=A7=80=20part=20?= =?UTF-8?q?=EA=B8=B0=EB=B0=98=20=EB=8B=A8=EC=9D=BC=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=EB=A1=9C=20=ED=86=B5=ED=95=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/leader/page.tsx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx index e95a057..cec81e8 100644 --- a/src/app/voting/leader/page.tsx +++ b/src/app/voting/leader/page.tsx @@ -1,18 +1,14 @@ "use client"; import { useState } from "react"; -import { TEAM_MEMBERS } from "@/constants/teams"; - -const LEADER_CANDIDATES = [ - ...TEAM_MEMBERS.frontend.Ditda, - ...TEAM_MEMBERS.frontend.JobDri, - ...TEAM_MEMBERS.frontend.Groupeat, - ...TEAM_MEMBERS.frontend.IPX, - ...TEAM_MEMBERS.frontend.CONX, -]; +import { TEAM_MEMBERS, type Part } from "@/constants/teams"; export default function VotingLeader() { const [selected, setSelected] = useState(null); + // 로그인 연동 후 로그인 정보의 part로 교체 + const part: Part = "frontend"; + const candidates = Object.values(TEAM_MEMBERS[part]).flat(); + const title = part === "backend" ? "BE - LEADER" : "FE - LEADER"; return (

    - FE - LEADER + {title}

      - {LEADER_CANDIDATES.map((name) => ( + {candidates.map((name) => (
    • From 9471c6ccf9638798d9d9d0422b58609ad46565f9 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 01:07:38 +0900 Subject: [PATCH 053/103] =?UTF-8?q?style:=20=EB=8D=B0=EB=AA=A8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=20=ED=8C=80=20=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=95=84=EC=9B=83=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/demoday/page.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/voting/demoday/page.tsx b/src/app/voting/demoday/page.tsx index b0c3ca1..640cc53 100644 --- a/src/app/voting/demoday/page.tsx +++ b/src/app/voting/demoday/page.tsx @@ -7,7 +7,7 @@ export default function VotingDemoday() { const [selected, setSelected] = useState(null); return ( -
      +
      투표하기 >}
      -
        +
          {TEAM_NAMES.map((team) => (
      + + +
      + + + +

      + DEMO-DAY +

      +
      + + + + +
      ); } From 4c688ac7076d95967ff580961a2462df8c914767 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 02:07:16 +0900 Subject: [PATCH 058/103] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=8C=8C=ED=8A=B8=20=EC=84=A0=ED=83=9D=20?= =?UTF-8?q?UI=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/members/page.tsx | 42 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index 154ed4d..09676aa 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -1,3 +1,43 @@ +"use client"; + +import { useState } from "react"; + +const ROLES = ["PM", "DESIGN", "FRONT-END", "BACK-END"] as const; +type Role = (typeof ROLES)[number]; + +const ROLE_LABELS: Record = { + PM: "PM", + DESIGN: "DESIGN", + "FRONT-END": "FRONT -\nEND", + "BACK-END": "BACK -\nEND", +}; + export default function Members() { - return

      MEMBERS

      ; + const [selected, setSelected] = useState(null); + + return ( +
      +
      + + + + + + {ROLES.map((role) => ( + + ))} +
      +
      + ); } From fe6ee1b7052c76d09d5eaca9d619d59c5165f9a5 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 02:09:27 +0900 Subject: [PATCH 059/103] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=ED=83=80=EC=9D=B4=ED=8B=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/members/page.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index 09676aa..1e680ce 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -17,6 +17,9 @@ export default function Members() { return (
      +

      + 23th MEMBERS +

      From 2c31b53f079101c31132b30f944d8c9b83f5fa57 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 02:22:04 +0900 Subject: [PATCH 060/103] =?UTF-8?q?feat:=20MemberCard=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/Ellipse 10.svg | 3 +++ public/Ellipse 9.svg | 3 +++ public/Intersect.svg | 3 +++ src/app/members/page.tsx | 4 ++++ src/components/MemberCard.tsx | 31 +++++++++++++++++++++++++++++++ 5 files changed, 44 insertions(+) create mode 100644 public/Ellipse 10.svg create mode 100644 public/Ellipse 9.svg create mode 100644 public/Intersect.svg create mode 100644 src/components/MemberCard.tsx diff --git a/public/Ellipse 10.svg b/public/Ellipse 10.svg new file mode 100644 index 0000000..f4650c6 --- /dev/null +++ b/public/Ellipse 10.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/Ellipse 9.svg b/public/Ellipse 9.svg new file mode 100644 index 0000000..b7e78aa --- /dev/null +++ b/public/Ellipse 9.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/Intersect.svg b/public/Intersect.svg new file mode 100644 index 0000000..ceb1f9e --- /dev/null +++ b/public/Intersect.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index 1e680ce..b9f2eb2 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useState } from "react"; +import MemberCard from "@/components/MemberCard"; const ROLES = ["PM", "DESIGN", "FRONT-END", "BACK-END"] as const; type Role = (typeof ROLES)[number]; @@ -20,6 +21,9 @@ export default function Members() {

      23th MEMBERS

      +
      + +
      diff --git a/src/components/MemberCard.tsx b/src/components/MemberCard.tsx new file mode 100644 index 0000000..53c70d2 --- /dev/null +++ b/src/components/MemberCard.tsx @@ -0,0 +1,31 @@ +export default function MemberCard() { + return ( +
      + + + + + + 이름 + + + {"학교\n과"} + +
      + ); +} From d9aca46c68559fb83e6e0eb68c0f9032ad3c0b7e Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 02:31:09 +0900 Subject: [PATCH 061/103] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=20=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4=ED=84=B0=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20=ED=8C=8C?= =?UTF-8?q?=ED=8A=B8=EB=B3=84=20=EC=B9=B4=EB=93=9C=20=EB=A0=8C=EB=8D=94?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/members/page.tsx | 21 +++++++---- src/components/MemberCard.tsx | 12 +++++-- src/constants/members.ts | 67 +++++++++++++++++++++++++++++++++++ 3 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 src/constants/members.ts diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index b9f2eb2..d67f18f 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -2,9 +2,9 @@ import { useState } from "react"; import MemberCard from "@/components/MemberCard"; +import { MEMBERS, type Role } from "@/constants/members"; -const ROLES = ["PM", "DESIGN", "FRONT-END", "BACK-END"] as const; -type Role = (typeof ROLES)[number]; +const ROLES: Role[] = ["PM", "DESIGN", "FRONT-END", "BACK-END"]; const ROLE_LABELS: Record = { PM: "PM", @@ -17,13 +17,22 @@ export default function Members() { const [selected, setSelected] = useState(null); return ( -
      +

      23th MEMBERS

      -
      - -
      + {selected && ( +
      + {MEMBERS[selected].map((member) => ( + + ))} +
      + )}
      diff --git a/src/components/MemberCard.tsx b/src/components/MemberCard.tsx index 53c70d2..f8dbfeb 100644 --- a/src/components/MemberCard.tsx +++ b/src/components/MemberCard.tsx @@ -1,4 +1,10 @@ -export default function MemberCard() { +type Props = { + name: string; + school: string; + department: string; +}; + +export default function MemberCard({ name, school, department }: Props) { return (
      - 이름 + {name} - {"학교\n과"} + {`${school}\n${department}`}
      ); diff --git a/src/constants/members.ts b/src/constants/members.ts new file mode 100644 index 0000000..2cb7452 --- /dev/null +++ b/src/constants/members.ts @@ -0,0 +1,67 @@ +export type Role = "PM" | "DESIGN" | "FRONT-END" | "BACK-END"; + +export type Member = { + name: string; + school: string; + department: string; +}; + +const DEFAULT_SCHOOL = "CEOS UNIV"; +const DEFAULT_DEPARTMENT = "Computer Science"; + +const m = (name: string): Member => ({ + name, + school: DEFAULT_SCHOOL, + department: DEFAULT_DEPARTMENT, +}); + +export const MEMBERS: Record = { + PM: [ + m("문현승"), + m("오유준"), + m("안민용"), + m("이소은"), + m("안세빈"), + m("안서연"), + m("이정원"), + m("변성우"), + m("조아현"), + m("김채원"), + ], + DESIGN: [ + m("문수인"), + m("고다현"), + m("김미소"), + m("오상헌"), + m("김예린"), + m("김은홍"), + m("김정원"), + m("권지민"), + m("우유민"), + m("이우림"), + ], + "FRONT-END": [ + m("박유민"), + m("권오진"), + m("이윤서"), + m("구민교"), + m("이승연"), + m("황영준"), + m("남기림"), + m("김민서"), + m("김홍엽"), + m("오유진"), + ], + "BACK-END": [ + m("임종훈"), + m("안준석"), + m("황신애"), + m("최우혁"), + m("김동욱"), + m("최승원"), + m("오지송"), + m("김태익"), + m("김태희"), + m("김도현"), + ], +}; From 7725be849448e554ff0e00b7cc38e59dcb940616 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 02:36:21 +0900 Subject: [PATCH 062/103] =?UTF-8?q?feat:=20=ED=88=AC=ED=91=9C=20=EA=B2=B0?= =?UTF-8?q?=EA=B3=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=20=EB=8F=99=EC=A0=81=20?= =?UTF-8?q?=EB=9D=BC=EC=9A=B0=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/result/[type]/page.tsx | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 src/app/voting/result/[type]/page.tsx diff --git a/src/app/voting/result/[type]/page.tsx b/src/app/voting/result/[type]/page.tsx new file mode 100644 index 0000000..60931b4 --- /dev/null +++ b/src/app/voting/result/[type]/page.tsx @@ -0,0 +1,14 @@ +import { notFound } from "next/navigation"; + +export default async function VotingResult({ + params, +}: { + params: Promise<{ type: string }>; +}) { + const { type } = await params; + if (type !== "leader" && type !== "demoday") notFound(); + + return ( +
      + ); +} From f884ce6c9a6801611866f681b515550702ca7027 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 02:43:32 +0900 Subject: [PATCH 063/103] =?UTF-8?q?refactor:=20=EB=A9=A4=EB=B2=84=20?= =?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A5=BC=20dev=20members=20?= =?UTF-8?q?=EA=B5=AC=EC=A1=B0=EC=97=90=20=EB=A7=9E=EC=B6=B0=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/members/page.tsx | 26 +++---- src/components/MemberCard.tsx | 10 ++- src/constants/members.ts | 130 +++++++++++++++++++--------------- 3 files changed, 94 insertions(+), 72 deletions(-) diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index d67f18f..37ccc80 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -2,19 +2,19 @@ import { useState } from "react"; import MemberCard from "@/components/MemberCard"; -import { MEMBERS, type Role } from "@/constants/members"; +import { MEMBERS, type Part } from "@/constants/members"; -const ROLES: Role[] = ["PM", "DESIGN", "FRONT-END", "BACK-END"]; +const PARTS: Part[] = ["PM", "DESIGN", "FRONTEND", "BACKEND"]; -const ROLE_LABELS: Record = { +const PART_LABELS: Record = { PM: "PM", DESIGN: "DESIGN", - "FRONT-END": "FRONT -\nEND", - "BACK-END": "BACK -\nEND", + FRONTEND: "FRONT -\nEND", + BACKEND: "BACK -\nEND", }; export default function Members() { - const [selected, setSelected] = useState(null); + const [selected, setSelected] = useState(null); return (
      @@ -23,7 +23,9 @@ export default function Members() { {selected && (
      - {MEMBERS[selected].map((member) => ( + {MEMBERS[selected] + .filter((member) => !member.isExecutive) + .map((member) => ( - {ROLES.map((role) => ( + {PARTS.map((part) => ( ))}
      diff --git a/src/components/MemberCard.tsx b/src/components/MemberCard.tsx index f8dbfeb..9b785a2 100644 --- a/src/components/MemberCard.tsx +++ b/src/components/MemberCard.tsx @@ -1,10 +1,14 @@ type Props = { name: string; - school: string; - department: string; + school?: string; + department?: string; }; -export default function MemberCard({ name, school, department }: Props) { +export default function MemberCard({ + name, + school = "CEOS UNIV", + department = "Computer Science", +}: Props) { return (
      ({ - name, - school: DEFAULT_SCHOOL, - department: DEFAULT_DEPARTMENT, -}); + isExecutive: boolean; + isLeader: boolean; + school?: string; + department?: string; +} -export const MEMBERS: Record = { +export const MEMBERS: Record = { PM: [ - m("문현승"), - m("오유준"), - m("안민용"), - m("이소은"), - m("안세빈"), - m("안서연"), - m("이정원"), - m("변성우"), - m("조아현"), - m("김채원"), + { name: "김채원", isExecutive: false, isLeader: false }, + { name: "문현승", isExecutive: false, isLeader: false }, + { name: "변성우", isExecutive: false, isLeader: false }, + { name: "안민용", isExecutive: false, isLeader: false }, + { name: "안서연", isExecutive: false, isLeader: false }, + { name: "안세빈", isExecutive: false, isLeader: false }, + { name: "오유준", isExecutive: false, isLeader: false }, + { name: "이소은", isExecutive: false, isLeader: false }, + { name: "이정원", isExecutive: false, isLeader: false }, + { name: "조아현", isExecutive: false, isLeader: false }, + { name: "현종혁", isExecutive: true, isLeader: true }, + { name: "박준영", isExecutive: true, isLeader: false }, + { name: "이우혁", isExecutive: true, isLeader: false }, + { name: "이혜린", isExecutive: true, isLeader: false }, + { name: "조은호", isExecutive: true, isLeader: false }, + { name: "허유진", isExecutive: true, isLeader: false }, ], DESIGN: [ - m("문수인"), - m("고다현"), - m("김미소"), - m("오상헌"), - m("김예린"), - m("김은홍"), - m("김정원"), - m("권지민"), - m("우유민"), - m("이우림"), + { name: "강예린", isExecutive: false, isLeader: false }, + { name: "고다현", isExecutive: false, isLeader: false }, + { name: "권지민", isExecutive: false, isLeader: false }, + { name: "김미소", isExecutive: false, isLeader: false }, + { name: "김은홍", isExecutive: false, isLeader: false }, + { name: "김정원", isExecutive: false, isLeader: false }, + { name: "문수인", isExecutive: false, isLeader: false }, + { name: "오상헌", isExecutive: false, isLeader: false }, + { name: "우유민", isExecutive: false, isLeader: false }, + { name: "이우림", isExecutive: false, isLeader: false }, + { name: "박서령", isExecutive: true, isLeader: true }, + { name: "노성주", isExecutive: true, isLeader: false }, + { name: "성유정", isExecutive: true, isLeader: false }, + { name: "윤시연", isExecutive: true, isLeader: false }, + { name: "정시빈", isExecutive: true, isLeader: false }, + { name: "정은선", isExecutive: true, isLeader: false }, + { name: "천영현", isExecutive: true, isLeader: false }, ], - "FRONT-END": [ - m("박유민"), - m("권오진"), - m("이윤서"), - m("구민교"), - m("이승연"), - m("황영준"), - m("남기림"), - m("김민서"), - m("김홍엽"), - m("오유진"), + FRONTEND: [ + { name: "구민교", isExecutive: false, isLeader: false }, + { name: "권오진", isExecutive: false, isLeader: false }, + { name: "김민서", isExecutive: false, isLeader: false }, + { name: "김홍엽", isExecutive: false, isLeader: false }, + { name: "남기림", isExecutive: false, isLeader: false }, + { name: "박유민", isExecutive: false, isLeader: false }, + { name: "오유진", isExecutive: false, isLeader: false }, + { name: "이승연", isExecutive: false, isLeader: false }, + { name: "이윤서", isExecutive: false, isLeader: false }, + { name: "황영준", isExecutive: false, isLeader: false }, + { name: "원채영", isExecutive: true, isLeader: true }, + { name: "김류원", isExecutive: true, isLeader: false }, + { name: "김윤성", isExecutive: true, isLeader: false }, + { name: "손주완", isExecutive: true, isLeader: false }, ], - "BACK-END": [ - m("임종훈"), - m("안준석"), - m("황신애"), - m("최우혁"), - m("김동욱"), - m("최승원"), - m("오지송"), - m("김태익"), - m("김태희"), - m("김도현"), + BACKEND: [ + { name: "김도현", isExecutive: false, isLeader: false }, + { name: "김동욱", isExecutive: false, isLeader: false }, + { name: "김태익", isExecutive: false, isLeader: false }, + { name: "김태희", isExecutive: false, isLeader: false }, + { name: "안준석", isExecutive: false, isLeader: false }, + { name: "오지송", isExecutive: false, isLeader: false }, + { name: "임종훈", isExecutive: false, isLeader: false }, + { name: "최승원", isExecutive: false, isLeader: false }, + { name: "최우혁", isExecutive: false, isLeader: false }, + { name: "황신애", isExecutive: false, isLeader: false }, + { name: "변호영", isExecutive: true, isLeader: true }, + { name: "신 혁", isExecutive: true, isLeader: false }, + { name: "이수아", isExecutive: true, isLeader: false }, + { name: "이연호", isExecutive: true, isLeader: false }, + { name: "이윤지", isExecutive: true, isLeader: false }, + { name: "이준영", isExecutive: true, isLeader: false }, ], }; From 15be69cfb847c14e0450619ee3400bc617b70234 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 02:57:02 +0900 Subject: [PATCH 064/103] =?UTF-8?q?feat:=20RankBadge=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/RankBadge.tsx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 src/components/RankBadge.tsx diff --git a/src/components/RankBadge.tsx b/src/components/RankBadge.tsx new file mode 100644 index 0000000..36e5f23 --- /dev/null +++ b/src/components/RankBadge.tsx @@ -0,0 +1,18 @@ +type Props = { + rank: number; + color: string; +}; + +export default function RankBadge({ rank, color }: Props) { + return ( +
      +
      + + {rank}위 + +
      + ); +} From f1a7c6a6cdecaf42cc7015ccb0667dd058e245c2 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 03:02:19 +0900 Subject: [PATCH 065/103] =?UTF-8?q?feat:=20VoteCount=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/VoteCount.tsx | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 src/components/VoteCount.tsx diff --git a/src/components/VoteCount.tsx b/src/components/VoteCount.tsx new file mode 100644 index 0000000..1a76549 --- /dev/null +++ b/src/components/VoteCount.tsx @@ -0,0 +1,16 @@ +type Props = { + name: string; + votes: number; + color: string; +}; + +export default function VoteCount({ name, votes, color }: Props) { + return ( +
      + {name} | {votes}표 +
      + ); +} From 49a37a97e82ea3ea71a421e334a7ecbf4bdc5db0 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 03:05:22 +0900 Subject: [PATCH 066/103] =?UTF-8?q?feat:=20=EA=B2=B0=EA=B3=BC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=88=9C=EC=9C=84=EB=B3=84=20=EB=93=9D?= =?UTF-8?q?=ED=91=9C=20=EB=B0=95=EC=8A=A4=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/result/[type]/page.tsx | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/src/app/voting/result/[type]/page.tsx b/src/app/voting/result/[type]/page.tsx index 60931b4..714147f 100644 --- a/src/app/voting/result/[type]/page.tsx +++ b/src/app/voting/result/[type]/page.tsx @@ -1,4 +1,6 @@ import { notFound } from "next/navigation"; +import RankBadge from "@/components/RankBadge"; +import VoteCount from "@/components/VoteCount"; export default async function VotingResult({ params, @@ -9,6 +11,26 @@ export default async function VotingResult({ if (type !== "leader" && type !== "demoday") notFound(); return ( -
      +
      + {/* TODO: API 연동 후 name, votes 교체 */} +
      + +
      +
      + +
      +
      + +
      +
      + +
      +
      + +
      +
      + +
      +
      ); } From 58f4910cc3dc573567291de6fdb20d68c613c811 Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 03:11:01 +0900 Subject: [PATCH 067/103] =?UTF-8?q?feat:=20=EA=B2=B0=EA=B3=BC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=B0=B0=EA=B2=BD=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/result/[type]/page.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/app/voting/result/[type]/page.tsx b/src/app/voting/result/[type]/page.tsx index 714147f..55dc778 100644 --- a/src/app/voting/result/[type]/page.tsx +++ b/src/app/voting/result/[type]/page.tsx @@ -12,6 +12,25 @@ export default async function VotingResult({ return (
      +
      +
      + + + + {/* TODO: API 연동 후 name, votes 교체 */}
      From 4278449b979d6b59c1ccd3ae82056f596ad8c1dd Mon Sep 17 00:00:00 2001 From: minseo Date: Mon, 22 Jun 2026 03:12:26 +0900 Subject: [PATCH 068/103] =?UTF-8?q?feat:=20=EA=B2=B0=EA=B3=BC=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EB=A9=94=EC=9D=B8=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=B2=84=ED=8A=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/result/[type]/page.tsx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/app/voting/result/[type]/page.tsx b/src/app/voting/result/[type]/page.tsx index 55dc778..c8067ab 100644 --- a/src/app/voting/result/[type]/page.tsx +++ b/src/app/voting/result/[type]/page.tsx @@ -1,4 +1,5 @@ import { notFound } from "next/navigation"; +import Link from "next/link"; import RankBadge from "@/components/RankBadge"; import VoteCount from "@/components/VoteCount"; @@ -20,6 +21,12 @@ export default async function VotingResult({ aria-hidden className="absolute top-[363px] left-[286px] w-[212px] h-[212px] pointer-events-none" /> + + 메인으로 가기 > + Date: Mon, 22 Jun 2026 03:16:27 +0900 Subject: [PATCH 069/103] =?UTF-8?q?feat:=20=ED=88=AC=ED=91=9C=ED=95=98?= =?UTF-8?q?=EA=B8=B0=20=EB=B2=84=ED=8A=BC=20=ED=81=B4=EB=A6=AD=20=EC=8B=9C?= =?UTF-8?q?=20=EA=B2=B0=EA=B3=BC=20=ED=8E=98=EC=9D=B4=EC=A7=80=EB=A1=9C=20?= =?UTF-8?q?=EC=9D=B4=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/demoday/page.tsx | 7 ++++++- src/app/voting/leader/page.tsx | 7 +++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/src/app/voting/demoday/page.tsx b/src/app/voting/demoday/page.tsx index 889cf3a..e40abd5 100644 --- a/src/app/voting/demoday/page.tsx +++ b/src/app/voting/demoday/page.tsx @@ -1,10 +1,12 @@ "use client"; import { useState } from "react"; +import { useRouter } from "next/navigation"; import { TEAM_NAMES, TeamName } from "@/constants/teams"; export default function VotingDemoday() { const [selected, setSelected] = useState(null); + const router = useRouter(); return (
      router.push("/voting/result/demoday")} + className={`absolute inset-0 flex items-center justify-center text-label1 disabled:cursor-default ${ + selected ? "cursor-pointer" : "" + }`} > {selected && <>투표하기 >} diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx index cec81e8..ace0217 100644 --- a/src/app/voting/leader/page.tsx +++ b/src/app/voting/leader/page.tsx @@ -1,10 +1,12 @@ "use client"; import { useState } from "react"; +import { useRouter } from "next/navigation"; import { TEAM_MEMBERS, type Part } from "@/constants/teams"; export default function VotingLeader() { const [selected, setSelected] = useState(null); + const router = useRouter(); // 로그인 연동 후 로그인 정보의 part로 교체 const part: Part = "frontend"; const candidates = Object.values(TEAM_MEMBERS[part]).flat(); @@ -72,8 +74,9 @@ export default function VotingLeader() {
      From 3182c1bbcbb19481a3ef7db51292511a0bd45f71 Mon Sep 17 00:00:00 2001 From: girimNam Date: Mon, 22 Jun 2026 15:37:19 +0900 Subject: [PATCH 070/103] =?UTF-8?q?chore:=20svg=20=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=9D=B4=EB=A6=84=20=ED=86=B5=EC=9D=BC=20=EB=B0=8F=20=ED=85=8C?= =?UTF-8?q?=EC=9D=BC=EC=9C=88=EB=93=9C=20=EC=8A=A4=EC=BC=80=EC=9D=BC=20?= =?UTF-8?q?=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../figure-ellipse-10.svg} | 0 .../figure-ellipse-15.svg} | 0 .../figure-ellipse-8.svg} | 0 .../figure-ellipse-9.svg} | 0 .../figure-intersect.svg} | 0 .../{Star 7.svg => figures/figure-star-7.svg} | 0 .../{Star 8.svg => figures/figure-star-8.svg} | 0 src/app/voting/demoday/page.tsx | 30 +++++++------ src/app/voting/leader/page.tsx | 34 +++++++-------- src/app/voting/page.tsx | 42 +++++++++---------- src/app/voting/result/[type]/page.tsx | 26 ++++++------ src/components/MemberCard.tsx | 20 ++++----- 12 files changed, 75 insertions(+), 77 deletions(-) rename public/{Ellipse 10.svg => figures/figure-ellipse-10.svg} (100%) rename public/{Ellipse 15.svg => figures/figure-ellipse-15.svg} (100%) rename public/{Ellipse 8.svg => figures/figure-ellipse-8.svg} (100%) rename public/{Ellipse 9.svg => figures/figure-ellipse-9.svg} (100%) rename public/{Intersect.svg => figures/figure-intersect.svg} (100%) rename public/{Star 7.svg => figures/figure-star-7.svg} (100%) rename public/{Star 8.svg => figures/figure-star-8.svg} (100%) diff --git a/public/Ellipse 10.svg b/public/figures/figure-ellipse-10.svg similarity index 100% rename from public/Ellipse 10.svg rename to public/figures/figure-ellipse-10.svg diff --git a/public/Ellipse 15.svg b/public/figures/figure-ellipse-15.svg similarity index 100% rename from public/Ellipse 15.svg rename to public/figures/figure-ellipse-15.svg diff --git a/public/Ellipse 8.svg b/public/figures/figure-ellipse-8.svg similarity index 100% rename from public/Ellipse 8.svg rename to public/figures/figure-ellipse-8.svg diff --git a/public/Ellipse 9.svg b/public/figures/figure-ellipse-9.svg similarity index 100% rename from public/Ellipse 9.svg rename to public/figures/figure-ellipse-9.svg diff --git a/public/Intersect.svg b/public/figures/figure-intersect.svg similarity index 100% rename from public/Intersect.svg rename to public/figures/figure-intersect.svg diff --git a/public/Star 7.svg b/public/figures/figure-star-7.svg similarity index 100% rename from public/Star 7.svg rename to public/figures/figure-star-7.svg diff --git a/public/Star 8.svg b/public/figures/figure-star-8.svg similarity index 100% rename from public/Star 8.svg rename to public/figures/figure-star-8.svg diff --git a/src/app/voting/demoday/page.tsx b/src/app/voting/demoday/page.tsx index e40abd5..c8a53b4 100644 --- a/src/app/voting/demoday/page.tsx +++ b/src/app/voting/demoday/page.tsx @@ -11,36 +11,34 @@ export default function VotingDemoday() { return (
      setSelected(null)} - className="relative min-h-screen bg-gradient-to-b from-[#FFFFFF] via-[#D2E6FD] to-[#FFFFFF] flex flex-col pt-[10rem] pb-[5rem] pl-[5rem] md:pt-[13.125rem] md:pb-[15.5rem] md:pl-[21.75rem]" + className="relative min-h-screen bg-linear-to-b from-[#FFFFFF] via-[#D2E6FD] to-[#FFFFFF] flex flex-col pt-40 pb-20 pl-20 md:pt-52.5 md:pb-62 md:pl-87" > -
      -
      +
      +
      -

      - DEMO-DAY -

      +

      DEMO-DAY

      -
      +
      투표하기 >}
      -
        +
          {TEAM_NAMES.map((team) => (
      diff --git a/src/app/voting/page.tsx b/src/app/voting/page.tsx index 3c7933c..f282c29 100644 --- a/src/app/voting/page.tsx +++ b/src/app/voting/page.tsx @@ -3,35 +3,35 @@ import type { Part } from "@/constants/teams"; export default function Voting() { // 로그인 연동 후 로그인 정보의 part로 교체 - const part: Part = "frontend"; + const part = "frontend" as Part; const partLabel = part === "backend" ? "BE" : "FE"; return ( -
      +
      -
      +
      -

      +

      {partLabel} - LEADER

      @@ -39,35 +39,35 @@ export default function Voting() { -
      +
      -

      +

      DEMO-DAY

      -
      -
      +
      +
      +
      메인으로 가기 > {/* TODO: API 연동 후 name, votes 교체 */} -
      +
      -
      +
      -
      +
      -
      +
      -
      +
      -
      +
      diff --git a/src/components/MemberCard.tsx b/src/components/MemberCard.tsx index 9b785a2..485de50 100644 --- a/src/components/MemberCard.tsx +++ b/src/components/MemberCard.tsx @@ -10,30 +10,30 @@ export default function MemberCard({ department = "Computer Science", }: Props) { return ( -
      +
      - - + + {name} - + {`${school}\n${department}`}
      From 26eef8eefb174917e9462b960e81f1e6d03d8a07 Mon Sep 17 00:00:00 2001 From: girimNam Date: Mon, 22 Jun 2026 16:16:38 +0900 Subject: [PATCH 071/103] =?UTF-8?q?chore:=20username->=20email=EB=A1=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/login/page.tsx | 50 ++++++++++++++++++++++------------- src/schemas/login.ts | 2 +- 2 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index ab44a14..48a60cc 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -6,6 +6,7 @@ import { zodResolver } from "@hookform/resolvers/zod"; import { loginSchema, LoginForm } from "@/schemas/login"; import { useAuth } from "@/contexts/AuthContext"; import { useRouter } from "next/navigation"; +import { useState } from "react"; export default function Login() { const { @@ -17,34 +18,45 @@ export default function Login() { resolver: zodResolver(loginSchema), mode: "onChange", defaultValues: { - username: "", + email: "", password: "", }, }); const router = useRouter(); const { login } = useAuth(); - const username = watch("username"); + const email = watch("email"); const password = watch("password"); + const [loginError, setLoginError] = useState(null); - const onSubmit = async (_data: LoginForm) => { + const onSubmit = async (data: LoginForm) => { + setLoginError(null); try { - /* 백엔드 연동 시 주석 해제 const res = await fetch("/api/auth/login", { method: "POST", headers: { "Content-Type": "application/json" }, - body: JSON.stringify(data), + body: JSON.stringify({ email: data.email, password: data.password }), credentials: "include", }); - const { accessToken } = await res.json(); - login(accessToken); - */ - // 임시: API 연동 전까지 더미 토큰으로 로그인 처리 - login("dummy-token"); + if (!res.ok) { + if (res.status === 401) { + setLoginError("이메일 또는 비밀번호가 올바르지 않습니다."); + } else if (res.status === 400) { + setLoginError("입력값을 확인해 주세요."); + } else if (res.status === 403) { + setLoginError("비활성화 또는 탈퇴된 계정입니다."); + } else { + setLoginError("로그인 중 오류가 발생했습니다. 다시 시도해 주세요."); + } + return; + } + + const { data: responseData } = await res.json(); + login(responseData.accessToken); router.push("/members"); - } catch (error) { - console.error("로그인 실패:", error); + } catch { + setLoginError("네트워크 오류가 발생했습니다. 다시 시도해 주세요."); } }; @@ -55,15 +67,13 @@ export default function Login() { @@ -81,6 +91,10 @@ export default function Login() {

      + {loginError && ( +

      {loginError}

      + )} + diff --git a/src/app/layout.tsx b/src/app/layout.tsx index da8834c..ba12397 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,6 +1,5 @@ import type { Metadata } from "next"; import { Geist, Geist_Mono } from "next/font/google"; -import { AuthProvider } from "@/contexts/AuthContext"; import "./globals.css"; const geistSans = Geist({ @@ -29,7 +28,7 @@ export default function RootLayout({ className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`} > - {children} + {children} ); diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 330cd63..9b45111 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -4,7 +4,8 @@ import Link from "next/link"; import Image from "next/image"; import { usePathname, useRouter } from "next/navigation"; import { useState } from "react"; -import { useAuth } from "@/contexts/AuthContext"; +import { useAuthStore } from "@/store/authStore"; +import { logout as logoutApi } from "@/api/auth"; interface NavItem { label: string; @@ -21,11 +22,16 @@ export default function NavBar({ className }: { className?: string }) { const pathname = usePathname(); const router = useRouter(); const [open, setOpen] = useState(false); - const { isLoggedIn, logout } = useAuth(); + const isLoggedIn = useAuthStore((s) => s.accessToken !== null); + const clearAuth = useAuthStore((s) => s.clearAuth); - const handleLogout = () => { - logout(); - router.push("/login"); + const handleLogout = async () => { + try { + await logoutApi(); + } finally { + clearAuth(); + router.push("/login"); + } }; return ( diff --git a/src/constants/api.ts b/src/constants/api.ts new file mode 100644 index 0000000..5640839 --- /dev/null +++ b/src/constants/api.ts @@ -0,0 +1,7 @@ +export const API_ENDPOINTS = { + AUTH: { + LOGIN: "/api/auth/login", + REISSUE: "/api/auth/reissue", + LOGOUT: "/api/auth/logout", + }, +} as const; diff --git a/src/contexts/AuthContext.tsx b/src/contexts/AuthContext.tsx deleted file mode 100644 index f9c6251..0000000 --- a/src/contexts/AuthContext.tsx +++ /dev/null @@ -1,38 +0,0 @@ -"use client"; - -import { createContext, useContext, useState } from "react"; -import { setAccessToken, clearAccessToken } from "@/lib/auth"; - -interface AuthContextType { - isLoggedIn: boolean; - login: (token: string) => void; - logout: () => void; -} - -const AuthContext = createContext(null); - -export function AuthProvider({ children }: { children: React.ReactNode }) { - const [isLoggedIn, setIsLoggedIn] = useState(false); - - const login = (token: string) => { - setAccessToken(token); - setIsLoggedIn(true); - }; - - const logout = () => { - clearAccessToken(); - setIsLoggedIn(false); - }; - - return ( - - {children} - - ); -} - -export function useAuth() { - const ctx = useContext(AuthContext); - if (!ctx) throw new Error("useAuth must be used within AuthProvider"); - return ctx; -} diff --git a/src/lib/api.ts b/src/lib/api.ts new file mode 100644 index 0000000..b561f94 --- /dev/null +++ b/src/lib/api.ts @@ -0,0 +1,45 @@ +import axios from "axios"; +import { useAuthStore } from "@/store/authStore"; +import { API_ENDPOINTS } from "@/constants/api"; + +export const api = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, + withCredentials: true, +}); + +api.interceptors.request.use((config) => { + const token = useAuthStore.getState().accessToken; + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +api.interceptors.response.use( + (response) => response, + async (error) => { + const originalRequest = error.config; + + if (error.response?.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + + try { + const { data } = await axios.post( + API_ENDPOINTS.AUTH.REISSUE, + {}, + { withCredentials: true }, + ); + const newToken = data.data.accessToken; + useAuthStore.getState().setAccessToken(newToken); + originalRequest.headers.Authorization = `Bearer ${newToken}`; + + return api(originalRequest); + } catch { + useAuthStore.getState().clearAuth(); + window.location.href = "/login"; + } + } + + return Promise.reject(error); + }, +); diff --git a/src/lib/auth.ts b/src/lib/auth.ts deleted file mode 100644 index a9698ee..0000000 --- a/src/lib/auth.ts +++ /dev/null @@ -1,28 +0,0 @@ -let accessToken: string | null = null; - -export const getAccessToken = () => accessToken; - -export const setAccessToken = (token: string) => { - accessToken = token; -}; - -export const clearAccessToken = () => { - accessToken = null; -}; - -export const silentRefresh = async (): Promise => { - try { - { - /* - const res = await fetch("/api/auth/reissue", { - method: "POST", - credentials: "include", - }); - const { accessToken: newToken } = await res.json(); - setAccessToken(newToken);*/ - } - return true; - } catch { - return false; - } -}; diff --git a/src/schemas/login.ts b/src/schemas/login.ts index 90e815f..6ad6023 100644 --- a/src/schemas/login.ts +++ b/src/schemas/login.ts @@ -1,7 +1,7 @@ import { z } from "zod"; export const loginSchema = z.object({ - email: z.string().min(1, "이메일을 입력해 주세요").email("올바른 이메일 형식을 입력해 주세요"), + username: z.string().min(1, "아이디를 입력해 주세요"), password: z.string().min(1, "비밀번호를 입력해 주세요"), }); diff --git a/src/store/authStore.ts b/src/store/authStore.ts new file mode 100644 index 0000000..cd56ba0 --- /dev/null +++ b/src/store/authStore.ts @@ -0,0 +1,25 @@ +import { create } from "zustand"; + +type User = { + userId: number; + username: string; + name: string; + part: string; + team: string; +}; + +type AuthState = { + accessToken: string | null; + user: User | null; + setAuth: (accessToken: string, user: User) => void; + setAccessToken: (accessToken: string) => void; + clearAuth: () => void; +}; + +export const useAuthStore = create((set) => ({ + accessToken: null, + user: null, + setAuth: (accessToken, user) => set({ accessToken, user }), + setAccessToken: (accessToken) => set({ accessToken }), + clearAuth: () => set({ accessToken: null, user: null }), +})); From 51569b282ba167210cd15e7ac040432f990ef9ba Mon Sep 17 00:00:00 2001 From: girimNam Date: Mon, 22 Jun 2026 17:26:35 +0900 Subject: [PATCH 073/103] =?UTF-8?q?chore:=20=ED=8C=8C=EC=9D=BC=20=EC=9D=B4?= =?UTF-8?q?=EB=A6=84=20=EC=A4=91=EB=B3=B5=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth.ts | 2 +- src/constants/{api.ts => endpoint.ts} | 0 src/lib/api.ts | 2 +- 3 files changed, 2 insertions(+), 2 deletions(-) rename src/constants/{api.ts => endpoint.ts} (100%) diff --git a/src/api/auth.ts b/src/api/auth.ts index f60a2f1..775994c 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,6 +1,6 @@ import axios from "axios"; import { useAuthStore } from "@/store/authStore"; -import { API_ENDPOINTS } from "@/constants/api"; +import { API_ENDPOINTS } from "@/constants/endpoint"; type LoginRequest = { username: string; diff --git a/src/constants/api.ts b/src/constants/endpoint.ts similarity index 100% rename from src/constants/api.ts rename to src/constants/endpoint.ts diff --git a/src/lib/api.ts b/src/lib/api.ts index b561f94..9bd0f24 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,6 +1,6 @@ import axios from "axios"; import { useAuthStore } from "@/store/authStore"; -import { API_ENDPOINTS } from "@/constants/api"; +import { API_ENDPOINTS } from "@/constants/endpoint"; export const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, From 6d7c099c74fb3e2b5c372325dbd5f263fd5db642 Mon Sep 17 00:00:00 2001 From: girimNam Date: Mon, 22 Jun 2026 22:28:03 +0900 Subject: [PATCH 074/103] =?UTF-8?q?fix:=20Members=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=20=EC=A4=91=EB=B3=B5=20=EC=A0=9C=EA=B1=B0=20=EB=B0=8F?= =?UTF-8?q?=20=EB=B0=98=EC=9D=91=ED=98=95=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/members/MemberCard.tsx | 25 ------- src/app/members/layout.tsx | 4 +- src/app/members/page.tsx | 115 +++++++++++++++++++++++---------- src/components/MemberCard.tsx | 24 ++----- 4 files changed, 90 insertions(+), 78 deletions(-) delete mode 100644 src/app/members/MemberCard.tsx diff --git a/src/app/members/MemberCard.tsx b/src/app/members/MemberCard.tsx deleted file mode 100644 index f820ef3..0000000 --- a/src/app/members/MemberCard.tsx +++ /dev/null @@ -1,25 +0,0 @@ -import Image from "next/image"; - -export default function MemberCard({ - name, - isLeader, -}: { - name: string; - isLeader?: boolean; -}) { - return ( -
      - -
      - {isLeader &&

      PART LEADER

      } -

      {name}

      -
      -
      - ); -} diff --git a/src/app/members/layout.tsx b/src/app/members/layout.tsx index b6b7def..26eb9f4 100644 --- a/src/app/members/layout.tsx +++ b/src/app/members/layout.tsx @@ -6,9 +6,9 @@ export default function MembersLayout({ children: React.ReactNode; }) { return ( -
      +
      -
      {children}
      + {children}
      ); } diff --git a/src/app/members/page.tsx b/src/app/members/page.tsx index 37ccc80..7e7904f 100644 --- a/src/app/members/page.tsx +++ b/src/app/members/page.tsx @@ -1,6 +1,7 @@ "use client"; import { useState } from "react"; +import Image from "next/image"; import MemberCard from "@/components/MemberCard"; import { MEMBERS, type Part } from "@/constants/members"; @@ -13,48 +14,96 @@ const PART_LABELS: Record = { BACKEND: "BACK -\nEND", }; +const PART_LABELS_MOBILE: Record = { + PM: "PM", + DESIGN: "DESIGN", + FRONTEND: "FRONT-END", + BACKEND: "BACK-END", +}; + export default function Members() { - const [selected, setSelected] = useState(null); + const [selected, setSelected] = useState("FRONTEND"); return ( -
      -

      - 23th MEMBERS -

      - {selected && ( +
      +
      +
      + +
      + {PARTS.map((part) => ( + + ))} +
      +
      + +

      + 23th MEMBERS +

      + +
      + {MEMBERS[selected] + .filter((m) => !m.isExecutive) + .map((m) => ( + + ))} +
      +
      + +
      +

      + 23th MEMBERS +

      {MEMBERS[selected] .filter((member) => !member.isExecutive) .map((member) => ( - + + ))} +
      +
      + + + + + + {PARTS.map((part) => ( + ))}
      - )} -
      - - - - - - {PARTS.map((part) => ( - - ))}
      ); diff --git a/src/components/MemberCard.tsx b/src/components/MemberCard.tsx index 485de50..dc2d651 100644 --- a/src/components/MemberCard.tsx +++ b/src/components/MemberCard.tsx @@ -10,30 +10,18 @@ export default function MemberCard({ department = "Computer Science", }: Props) { return ( -
      +
      - - - - + + {name} - + {`${school}\n${department}`}
      From 944e268e563038fe9e2d4947d0c50bc2df266e26 Mon Sep 17 00:00:00 2001 From: girimNam Date: Mon, 22 Jun 2026 22:30:03 +0900 Subject: [PATCH 075/103] =?UTF-8?q?chore:=20=ED=88=AC=ED=91=9C=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EA=B8=80=EC=9E=90=20=EC=A1=B0=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/layout.tsx | 14 ++++++++++++++ src/app/voting/leader/page.tsx | 2 +- src/components/NavBar.tsx | 2 +- 3 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/app/voting/layout.tsx diff --git a/src/app/voting/layout.tsx b/src/app/voting/layout.tsx new file mode 100644 index 0000000..adb9ee4 --- /dev/null +++ b/src/app/voting/layout.tsx @@ -0,0 +1,14 @@ +import NavBar from "@/components/NavBar"; + +export default function VotingLayout({ + children, +}: { + children: React.ReactNode; +}) { + return ( + <> + + {children} + + ); +} diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx index 0ca9557..cbc0067 100644 --- a/src/app/voting/leader/page.tsx +++ b/src/app/voting/leader/page.tsx @@ -52,7 +52,7 @@ export default function VotingLeader() { e.stopPropagation(); setSelected(name); }} - className={`relative text-label1 cursor-pointer px-2 py-2 md:px-6 ${ + className={`relative text-label1 cursor-pointer px-2 py-2 md:px-6 whitespace-nowrap ${ selected === name ? "z-10" : "" }`} > diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 264f4e4..738c550 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -11,7 +11,7 @@ interface NavItem { } const NAV_ITEMS: NavItem[] = [ - { label: "VOTING", href: "#" }, + { label: "VOTING", href: "/voting" }, { label: "MEMBERS", href: "/members" }, { label: "ABOUT US", href: "#" }, { label: "LOGIN", href: "/login" }, From 290c7253a3099e02cae38c7e9079c2fe9ea563b5 Mon Sep 17 00:00:00 2001 From: girimNam Date: Mon, 22 Jun 2026 22:32:44 +0900 Subject: [PATCH 076/103] =?UTF-8?q?chore:=20=EC=84=B8=EB=A1=9C=20=EC=8A=A4?= =?UTF-8?q?=ED=81=AC=EB=A1=A4=EB=B0=94=20hidden=20=EC=84=A4=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/globals.css | 8 ++++++++ src/app/members/layout.tsx | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/app/globals.css b/src/app/globals.css index 9fe6bf9..ae79927 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -72,3 +72,11 @@ body { color: var(--foreground); font-family: "Pretendard", Arial, Helvetica, sans-serif; } + +.no-scrollbar { + scrollbar-width: none; +} + +.no-scrollbar::-webkit-scrollbar { + display: none; +} diff --git a/src/app/members/layout.tsx b/src/app/members/layout.tsx index 26eb9f4..d686716 100644 --- a/src/app/members/layout.tsx +++ b/src/app/members/layout.tsx @@ -6,7 +6,7 @@ export default function MembersLayout({ children: React.ReactNode; }) { return ( -
      +
      {children}
      From 08d6c67a11b0307400eca1dcdd0f61fa029d7fe9 Mon Sep 17 00:00:00 2001 From: girimNam Date: Tue, 23 Jun 2026 21:34:15 +0900 Subject: [PATCH 077/103] =?UTF-8?q?feat:=20=EB=8D=B0=EB=AA=A8=EB=8D=B0?= =?UTF-8?q?=EC=9D=B4,=20=ED=8C=8C=ED=8A=B8=EC=9E=A5=20=ED=88=AC=ED=91=9C?= =?UTF-8?q?=20api=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth.ts | 2 + src/api/vote.ts | 77 +++++++++++++++ src/app/voting/demoday/page.tsx | 60 +++++++++++- src/app/voting/leader/page.tsx | 78 +++++++++++++-- src/app/voting/page.tsx | 11 ++- src/app/voting/result/[type]/page.tsx | 134 +++++++++++++++++++++----- src/constants/endpoint.ts | 13 ++- src/lib/api.ts | 2 + 8 files changed, 333 insertions(+), 44 deletions(-) create mode 100644 src/api/vote.ts diff --git a/src/api/auth.ts b/src/api/auth.ts index 775994c..7dc5267 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -2,6 +2,8 @@ import axios from "axios"; import { useAuthStore } from "@/store/authStore"; import { API_ENDPOINTS } from "@/constants/endpoint"; +const BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? ""; + type LoginRequest = { username: string; password: string; diff --git a/src/api/vote.ts b/src/api/vote.ts new file mode 100644 index 0000000..9cdc456 --- /dev/null +++ b/src/api/vote.ts @@ -0,0 +1,77 @@ +import { api } from "@/lib/api"; +import { API_ENDPOINTS } from "@/constants/endpoint"; + +type VotePartLeaderResponse = { + success: boolean; + data: { closed: boolean } | null; + error: { status: number; code: string; message: string } | null; +}; + +export async function votePartLeader(name: string): Promise { + const { data } = await api.post( + API_ENDPOINTS.VOTES.PART_LEADER, + { name }, + ); + return data; +} + +type Ranking = { + rank: number; + name: string; + votes: number; +}; + +type PartLeaderResultResponse = { + success: boolean; + data: { + closed: boolean; + totalVotes: number; + part: string; + rankings: Ranking[]; + } | null; + error: { status: number; code: string; message: string } | null; +}; + +type VoteDemoDayResponse = { + success: boolean; + data: { closed: boolean } | null; + error: { status: number; code: string; message: string } | null; +}; + +export async function voteDemoDay(team: string): Promise { + const { data } = await api.post( + API_ENDPOINTS.VOTES.DEMO_DAY, + { team }, + ); + return data; +} + +export async function getPartLeaderResult(part: string): Promise { + const { data } = await api.get( + `${API_ENDPOINTS.VOTES.PART_LEADER_RESULT}?part=${part}`, + ); + return data; +} + +type DemoDayRanking = { + rank: number; + team: string; + votes: number; +}; + +type DemoDayResultResponse = { + success: boolean; + data: { + closed: boolean; + totalVotes: number; + rankings: DemoDayRanking[]; + } | null; + error: { status: number; code: string; message: string } | null; +}; + +export async function getDemoDayResult(): Promise { + const { data } = await api.get( + API_ENDPOINTS.VOTES.DEMO_DAY_RESULT, + ); + return data; +} diff --git a/src/app/voting/demoday/page.tsx b/src/app/voting/demoday/page.tsx index c8a53b4..ea6b247 100644 --- a/src/app/voting/demoday/page.tsx +++ b/src/app/voting/demoday/page.tsx @@ -3,10 +3,49 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { TEAM_NAMES, TeamName } from "@/constants/teams"; +import { voteDemoDay } from "@/api/vote"; +import { useAuthStore } from "@/store/authStore"; export default function VotingDemoday() { const [selected, setSelected] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [voteError, setVoteError] = useState(null); const router = useRouter(); + const userTeam = useAuthStore((state) => state.user?.team); + + const handleVote = async () => { + if (!selected || isLoading) return; + setVoteError(null); + setIsLoading(true); + + try { + await voteDemoDay(selected); + router.push("/voting/result/demoday"); + } catch (err: unknown) { + //에러 타입 별로 정리 + if (err && typeof err === "object" && "response" in err) { + const axiosErr = err as { + response: { status: number; data?: { error?: { code?: string } } }; + }; + const status = axiosErr.response.status; + const code = axiosErr.response.data?.error?.code; + + if (status === 409 || code === "V005") { + setVoteError("이미 투표하셨습니다."); + } else if (status === 410 || code === "V006") { + setVoteError("이미 마감된 투표입니다."); + } else if (code === "V004") { + setVoteError("본인 팀에는 투표할 수 없습니다."); + } else if (code === "V001") { + setVoteError("유효하지 않은 팀입니다."); + } else { + setVoteError("투표 중 오류가 발생했습니다. 다시 시도해주세요."); + } + } + } finally { + setIsLoading(false); + } + }; return (
      DEMO-DAY

      +
      + + {voteError && ( +

      + {voteError} +

      + )} +
        @@ -69,6 +116,11 @@ export default function VotingDemoday() { type="button" onClick={(e) => { e.stopPropagation(); + if (userTeam && team.toLowerCase() === userTeam.toLowerCase()) { + setVoteError("본인 팀에는 투표할 수 없습니다."); + return; + } + setVoteError(null); setSelected(team); }} className={`relative text-label1 cursor-pointer px-6 py-2 ${ diff --git a/src/app/voting/leader/page.tsx b/src/app/voting/leader/page.tsx index cbc0067..ea64e86 100644 --- a/src/app/voting/leader/page.tsx +++ b/src/app/voting/leader/page.tsx @@ -3,19 +3,62 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; import { TEAM_MEMBERS, type Part } from "@/constants/teams"; +import { votePartLeader } from "@/api/vote"; +import { useAuthStore } from "@/store/authStore"; export default function VotingLeader() { const [selected, setSelected] = useState(null); + const [isLoading, setIsLoading] = useState(false); + const [voteError, setVoteError] = useState(null); const router = useRouter(); - // 로그인 연동 후 로그인 정보의 part로 교체 - const part = "frontend" as Part; + + const userPart = useAuthStore((state) => state.user?.part?.toLowerCase()) as + | Part + | undefined; + const userName = useAuthStore((state) => state.user?.name); + const part: Part = userPart ?? "frontend"; const candidates = Object.values(TEAM_MEMBERS[part]).flat(); const title = part === "backend" ? "BE - LEADER" : "FE - LEADER"; + const handleVote = async () => { + if (!selected || isLoading) return; + setVoteError(null); + setIsLoading(true); + + try { + await votePartLeader(selected); + router.push(`/voting/result/leader?part=${part.toUpperCase()}`); + } catch (err: unknown) { + if (err && typeof err === "object" && "response" in err) { + const axiosErr = err as { + response: { status: number; data?: { error?: { code?: string } } }; + }; + const status = axiosErr.response.status; + const code = axiosErr.response.data?.error?.code; + + if (status === 409 || code === "V005") { + setVoteError("이미 투표하셨습니다."); + } else if (status === 410 || code === "V006") { + setVoteError("이미 마감된 투표입니다."); + } else if (code === "V002") { + setVoteError("본인 파트의 후보에게만 투표할 수 있습니다."); + } else if (code === "V003") { + setVoteError("본인에게는 투표할 수 없습니다."); + } else if (code === "U002") { + setVoteError("해당 후보를 찾을 수 없습니다."); + } else { + setVoteError("투표 중 오류가 발생했습니다. 다시 시도해주세요."); + } + } + } finally { + setIsLoading(false); + } + }; + return (
        setSelected(null)} - className="relative min-h-screen bg-gradient-to-b from-[#FFFFFF] via-[#D2E6FD] to-[#FFFFFF]" + className="relative min-h-screen bg-linear-to-b from-[#FFFFFF] via-[#D2E6FD] to-[#FFFFFF]" >
        @@ -37,9 +80,7 @@ export default function VotingLeader() { aria-hidden className="absolute -top-12 -left-11 w-33 h-33 pointer-events-none" /> -

        - {title} -

        +

        {title}

        @@ -50,6 +91,11 @@ export default function VotingLeader() { type="button" onClick={(e) => { e.stopPropagation(); + if (userName && name === userName) { + setVoteError("본인에게는 투표할 수 없습니다."); + return; + } + setVoteError(null); setSelected(name); }} className={`relative text-label1 cursor-pointer px-2 py-2 md:px-6 whitespace-nowrap ${ @@ -71,19 +117,31 @@ export default function VotingLeader() { ))}
      + {voteError && ( +

      + {voteError} +

      + )} +
      ); diff --git a/src/app/voting/page.tsx b/src/app/voting/page.tsx index f282c29..d396a30 100644 --- a/src/app/voting/page.tsx +++ b/src/app/voting/page.tsx @@ -1,9 +1,12 @@ import Link from "next/link"; import type { Part } from "@/constants/teams"; +import { useAuthStore } from "@/store/authStore"; export default function Voting() { - // 로그인 연동 후 로그인 정보의 part로 교체 - const part = "frontend" as Part; + const userPart = useAuthStore((state) => state.user?.part?.toLowerCase()) as + | Part + | undefined; + const part: Part = userPart ?? "frontend"; const partLabel = part === "backend" ? "BE" : "FE"; return ( @@ -60,9 +63,7 @@ export default function Voting() { aria-hidden className="absolute -bottom-9 -right-9 w-27.25 h-27 pointer-events-none" /> -

      - DEMO-DAY -

      +

      DEMO-DAY

      diff --git a/src/app/voting/result/[type]/page.tsx b/src/app/voting/result/[type]/page.tsx index cc404c2..8368db4 100644 --- a/src/app/voting/result/[type]/page.tsx +++ b/src/app/voting/result/[type]/page.tsx @@ -1,15 +1,101 @@ +"use client"; + +import { useEffect, useState } from "react"; import { notFound } from "next/navigation"; +import { useSearchParams } from "next/navigation"; import Link from "next/link"; import RankBadge from "@/components/RankBadge"; import VoteCount from "@/components/VoteCount"; +import { getPartLeaderResult, getDemoDayResult } from "@/api/vote"; + +type Ranking = { rank: number; name: string; votes: number }; + +const RANK_POSITIONS = [ + { + voteCount: "absolute top-43 left-97.75", + badge: "absolute top-35.25 left-87", + color: "#E3E8F5", + badgeColor: "#1B7BE8", + }, + { + voteCount: "absolute top-104 left-170.75", + badge: "absolute top-96.25 left-160", + color: "#F2F4F6", + badgeColor: "#FFEFB1", + }, + { + voteCount: "absolute top-170 left-108.75", + badge: "absolute top-157.25 left-98", + color: "#F2F9F9", + badgeColor: "rgba(223, 70, 70, 0.57)", + }, +]; -export default async function VotingResult({ +export default function VotingResult({ params, }: { params: Promise<{ type: string }>; }) { - const { type } = await params; - if (type !== "leader" && type !== "demoday") notFound(); + const searchParams = useSearchParams(); + const part = searchParams.get("part") ?? "FRONTEND"; + + const [rankings, setRankings] = useState([]); + const [loading, setLoading] = useState(true); + const [notClosed, setNotClosed] = useState(false); + const [typeResolved, setTypeResolved] = useState(null); + + useEffect(() => { + params.then(({ type }) => { + if (type !== "leader" && type !== "demoday") { + notFound(); + } + setTypeResolved(type); + }); + }, [params]); + + useEffect(() => { + if (!typeResolved) return; + + const fetchResult = async () => { + try { + if (typeResolved === "leader") { + const res = await getPartLeaderResult(part); + if (res.data) + setRankings( + res.data.rankings + .slice(0, 3) + .map((r) => ({ rank: r.rank, name: r.name, votes: r.votes })), + ); + } else { + const res = await getDemoDayResult(); + if (res.data) + setRankings( + res.data.rankings + .slice(0, 3) + .map((r) => ({ rank: r.rank, name: r.team, votes: r.votes })), + ); + } + } catch (err: unknown) { + if (err && typeof err === "object" && "response" in err) { + const axiosErr = err as { response: { status: number } }; + if (axiosErr.response.status === 423) { + setNotClosed(true); + } + } + } finally { + setLoading(false); + } + }; + + fetchResult(); + }, [typeResolved, part]); + + const top3 = RANK_POSITIONS.map((pos, i) => ({ + ...pos, + name: rankings[i]?.name ?? "-", + votes: rankings[i]?.votes ?? 0, + rank: i + 1, + })); return (
      @@ -38,25 +124,29 @@ export default async function VotingResult({ fill="rgba(223, 70, 70, 0.57)" /> - {/* TODO: API 연동 후 name, votes 교체 */} -
      - -
      -
      - -
      -
      - -
      -
      - -
      -
      - -
      -
      - -
      + + {!loading && notClosed && ( +

      + 아직 투표가 마감되지 않았습니다. +

      + )} + + {!loading && + !notClosed && + top3.map((item) => ( +
      +
      + +
      +
      + +
      +
      + ))}
      ); } diff --git a/src/constants/endpoint.ts b/src/constants/endpoint.ts index 5640839..e746e78 100644 --- a/src/constants/endpoint.ts +++ b/src/constants/endpoint.ts @@ -1,7 +1,14 @@ export const API_ENDPOINTS = { AUTH: { - LOGIN: "/api/auth/login", - REISSUE: "/api/auth/reissue", - LOGOUT: "/api/auth/logout", + LOGIN: "/auth/login", + REISSUE: "/auth/reissue", + LOGOUT: "/auth/logout", + }, + + VOTES: { + PART_LEADER: "/votes/part-leader", + PART_LEADER_RESULT: "/votes/part-leader/result", + DEMO_DAY: "/votes/demo-day", + DEMO_DAY_RESULT: "/votes/demo-day/result", }, } as const; diff --git a/src/lib/api.ts b/src/lib/api.ts index 9bd0f24..735f027 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -2,11 +2,13 @@ import axios from "axios"; import { useAuthStore } from "@/store/authStore"; import { API_ENDPOINTS } from "@/constants/endpoint"; +//맨처음에 백엔드 ai 자겨오는 부분 export const api = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL, withCredentials: true, }); +//api 인터셉터 부분 api.interceptors.request.use((config) => { const token = useAuthStore.getState().accessToken; if (token) { From 0f0be8a7bb9f255de1bb6b1df4a92815b62a1672 Mon Sep 17 00:00:00 2001 From: girimNam Date: Tue, 23 Jun 2026 21:54:53 +0900 Subject: [PATCH 078/103] =?UTF-8?q?fix:=20use=20client=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/page.tsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/app/voting/page.tsx b/src/app/voting/page.tsx index d396a30..bb25f28 100644 --- a/src/app/voting/page.tsx +++ b/src/app/voting/page.tsx @@ -1,3 +1,5 @@ +"use client"; + import Link from "next/link"; import type { Part } from "@/constants/teams"; import { useAuthStore } from "@/store/authStore"; From 504e1b1d45732e8c22c5fb806ea1801ce78ec12d Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:01:03 +0900 Subject: [PATCH 079/103] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20API=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth.ts | 33 +++++++++++++++++++++++++++++++++ src/constants/endpoint.ts | 1 + 2 files changed, 34 insertions(+) diff --git a/src/api/auth.ts b/src/api/auth.ts index 7dc5267..3a952fa 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -4,6 +4,32 @@ import { API_ENDPOINTS } from "@/constants/endpoint"; const BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? ""; +type SignupRequest = { + username: string; + password: string; + passwordConfirm: string; + email: string; + name: string; + part: string; + team: string; +}; + +type SignupResponse = { + success: boolean; + data: { + userId: number; + username: string; + name: string; + part: string; + team: string; + } | null; + error: { + status: number; + code: string; + message: string; + } | null; +}; + type LoginRequest = { username: string; password: string; @@ -44,6 +70,13 @@ type ReissueResponse = { } | null; }; +export async function signup(body: SignupRequest): Promise { + const { data } = await axios.post(API_ENDPOINTS.AUTH.SIGNUP, body, { + withCredentials: true, + }); + return data; +} + export async function login(body: LoginRequest): Promise { const { data } = await axios.post(API_ENDPOINTS.AUTH.LOGIN, body, { withCredentials: true, diff --git a/src/constants/endpoint.ts b/src/constants/endpoint.ts index e746e78..4cd0cb9 100644 --- a/src/constants/endpoint.ts +++ b/src/constants/endpoint.ts @@ -1,5 +1,6 @@ export const API_ENDPOINTS = { AUTH: { + SIGNUP: "/auth/signup", LOGIN: "/auth/login", REISSUE: "/auth/reissue", LOGOUT: "/auth/logout", From e28e0a76d53192192648c3fffa128b3c6dd4b5e0 Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:03:02 +0900 Subject: [PATCH 080/103] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=ED=8F=BC=20=EC=A0=9C=EC=B6=9C=20=EC=8B=9C=20API=20?= =?UTF-8?q?=EC=97=B0=EB=8F=99=20=EB=B0=8F=20=EC=97=90=EB=9F=AC=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 71 +++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 5 deletions(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 45aa34f..015ccb1 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -1,10 +1,13 @@ "use client"; import { useEffect, useRef, useState } from "react"; +import { useRouter } from "next/navigation"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; +import axios from "axios"; import { TEAM_MEMBERS, TEAM_NAMES } from "@/constants/teams"; import { signupSchema, SignupForm } from "@/schemas/signup"; +import { signup } from "@/api/auth"; type DropdownProps = { label: string; @@ -78,12 +81,16 @@ function Dropdown({ } export default function Signup() { + const router = useRouter(); + const [submitting, setSubmitting] = useState(false); + const [serverError, setServerError] = useState(null); const { register, control, handleSubmit, watch, setValue, + setError, formState: { errors }, } = useForm({ resolver: zodResolver(signupSchema), @@ -116,9 +123,56 @@ export default function Signup() { setValue("member", ""); }; - const onSubmit = (data: SignupForm) => { - console.log(data); - // TODO: 회원가입 API 연동 + const onSubmit = async (data: SignupForm) => { + setServerError(null); + setSubmitting(true); + try { + const res = await signup({ + username: data.username, + password: data.password, + passwordConfirm: data.passwordRe, + email: data.email, + name: data.member, + part: data.part.toUpperCase(), + team: data.team, + }); + + if (res.success) { + router.push("/login"); + return; + } + + handleSignupError(res.error?.code, res.error?.message); + } catch (err) { + if (axios.isAxiosError(err)) { + const apiError = err.response?.data?.error; + handleSignupError(apiError?.code, apiError?.message); + } else { + setServerError("회원가입에 실패했습니다. 잠시 후 다시 시도해 주세요."); + } + } finally { + setSubmitting(false); + } + }; + + const handleSignupError = (code?: string, message?: string) => { + switch (code) { + case "U003": + setError("username", { message: message ?? "이미 사용 중인 아이디입니다." }); + break; + case "U004": + setError("email", { message: message ?? "이미 사용 중인 이메일입니다." }); + break; + case "U001": + setError("passwordRe", { message: message ?? "비밀번호가 일치하지 않습니다." }); + break; + case "U002": + case "U005": + setServerError(message ?? "선택한 후보 정보가 올바르지 않습니다."); + break; + default: + setServerError(message ?? "회원가입에 실패했습니다."); + } }; return ( @@ -238,11 +292,18 @@ export default function Signup() { : " "}

      + {serverError && ( +

      + {serverError} +

      + )} +
      From c6dcc6e01404da00e479b51ca6a45f38764f7b69 Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:10:26 +0900 Subject: [PATCH 081/103] =?UTF-8?q?fix:=20signup=20=EC=9A=94=EC=B2=AD?= =?UTF-8?q?=EC=9D=B4=20baseURL=EC=9D=84=20=EC=82=AC=EC=9A=A9=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20api=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4?= =?UTF-8?q?=EB=A1=9C=20=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth.ts | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/api/auth.ts b/src/api/auth.ts index 3a952fa..ba9e2bc 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,4 +1,5 @@ import axios from "axios"; +import { api } from "@/lib/api"; import { useAuthStore } from "@/store/authStore"; import { API_ENDPOINTS } from "@/constants/endpoint"; @@ -71,9 +72,10 @@ type ReissueResponse = { }; export async function signup(body: SignupRequest): Promise { - const { data } = await axios.post(API_ENDPOINTS.AUTH.SIGNUP, body, { - withCredentials: true, - }); + const { data } = await api.post( + API_ENDPOINTS.AUTH.SIGNUP, + body, + ); return data; } From 5e411cc3cde6beed081930ce6b18942b9e74d3f1 Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:16:12 +0900 Subject: [PATCH 082/103] =?UTF-8?q?feat:=20=ED=9B=84=EB=B3=B4=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/candidate.ts | 32 ++++++++++++++++++++++++++++++++ src/constants/endpoint.ts | 2 ++ 2 files changed, 34 insertions(+) create mode 100644 src/api/candidate.ts diff --git a/src/api/candidate.ts b/src/api/candidate.ts new file mode 100644 index 0000000..3aab922 --- /dev/null +++ b/src/api/candidate.ts @@ -0,0 +1,32 @@ +import { api } from "@/lib/api"; +import { API_ENDPOINTS } from "@/constants/endpoint"; + +export type Candidate = { + name: string; + part: string; + team: string; +}; + +type CandidatesResponse = { + success: boolean; + data: Candidate[] | null; + error: { + status: number; + code: string; + message: string; + } | null; +}; + +type CandidatesParams = { + part?: string; + team?: string; +}; + +export async function getCandidates( + params: CandidatesParams = {}, +): Promise { + const { data } = await api.get(API_ENDPOINTS.CANDIDATES, { + params, + }); + return data; +} diff --git a/src/constants/endpoint.ts b/src/constants/endpoint.ts index 4cd0cb9..3720051 100644 --- a/src/constants/endpoint.ts +++ b/src/constants/endpoint.ts @@ -6,6 +6,8 @@ export const API_ENDPOINTS = { LOGOUT: "/auth/logout", }, + CANDIDATES: "/candidates", + VOTES: { PART_LEADER: "/votes/part-leader", PART_LEADER_RESULT: "/votes/part-leader/result", From b05eb6d89f49cb6e10b098ab2bb0440792949d29 Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:17:14 +0900 Subject: [PATCH 083/103] =?UTF-8?q?feat:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=9D=B4=EB=A6=84=20dropdown=EC=9D=84=20=ED=9B=84?= =?UTF-8?q?=EB=B3=B4=20=EB=AA=A9=EB=A1=9D=20API=20=EC=97=B0=EB=8F=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 33 ++++++++++++++++++++++++++++----- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 015ccb1..747eb44 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -5,9 +5,10 @@ import { useRouter } from "next/navigation"; import { Controller, useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import axios from "axios"; -import { TEAM_MEMBERS, TEAM_NAMES } from "@/constants/teams"; +import { TEAM_NAMES } from "@/constants/teams"; import { signupSchema, SignupForm } from "@/schemas/signup"; import { signup } from "@/api/auth"; +import { getCandidates } from "@/api/candidate"; type DropdownProps = { label: string; @@ -111,10 +112,32 @@ export default function Signup() { const email = watch("email"); const passwordRe = watch("passwordRe"); - const memberOptions = - team && TEAM_MEMBERS[part][team as keyof (typeof TEAM_MEMBERS)["frontend"]] - ? TEAM_MEMBERS[part][team as keyof (typeof TEAM_MEMBERS)["frontend"]] - : []; + const [memberOptions, setMemberOptions] = useState([]); + + useEffect(() => { + if (!team) { + setMemberOptions([]); + return; + } + + let cancelled = false; + (async () => { + try { + const res = await getCandidates({ + part: part.toUpperCase(), + team, + }); + if (cancelled) return; + setMemberOptions(res.success && res.data ? res.data.map((c) => c.name) : []); + } catch { + if (!cancelled) setMemberOptions([]); + } + })(); + + return () => { + cancelled = true; + }; + }, [part, team]); const handlePartChange = (next: SignupForm["part"]) => { if (next === part) return; From c9473acf7f61302e98c802db280325ac718b05a6 Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:24:20 +0900 Subject: [PATCH 084/103] =?UTF-8?q?refactor:=20auth.ts=20=EB=AF=B8?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=20=EC=BD=94=EB=93=9C=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20api=20=EC=9D=B8=EC=8A=A4=ED=84=B4=EC=8A=A4=EB=A1=9C?= =?UTF-8?q?=20=ED=86=B5=EC=9D=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/api/auth.ts | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/src/api/auth.ts b/src/api/auth.ts index ba9e2bc..345617f 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -1,10 +1,6 @@ -import axios from "axios"; import { api } from "@/lib/api"; -import { useAuthStore } from "@/store/authStore"; import { API_ENDPOINTS } from "@/constants/endpoint"; -const BASE = process.env.NEXT_PUBLIC_API_BASE_URL ?? ""; - type SignupRequest = { username: string; password: string; @@ -80,23 +76,15 @@ export async function signup(body: SignupRequest): Promise { } export async function login(body: LoginRequest): Promise { - const { data } = await axios.post(API_ENDPOINTS.AUTH.LOGIN, body, { - withCredentials: true, - }); + const { data } = await api.post(API_ENDPOINTS.AUTH.LOGIN, body); return data; } export async function reissue(): Promise { - const { data } = await axios.post(API_ENDPOINTS.AUTH.REISSUE, {}, { - withCredentials: true, - }); + const { data } = await api.post(API_ENDPOINTS.AUTH.REISSUE, {}); return data; } export async function logout(): Promise { - const token = useAuthStore.getState().accessToken; - await axios.post(API_ENDPOINTS.AUTH.LOGOUT, {}, { - withCredentials: true, - headers: { Authorization: `Bearer ${token}` }, - }); + await api.post(API_ENDPOINTS.AUTH.LOGOUT, {}); } From aa2b6f0bec41187150a1e79b87499e346e493c91 Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:25:45 +0900 Subject: [PATCH 085/103] =?UTF-8?q?fix:=20=ED=86=A0=ED=81=B0=20=EC=9E=AC?= =?UTF-8?q?=EB=B0=9C=EA=B8=89=20=EC=9A=94=EC=B2=AD=EC=97=90=20baseURL=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/lib/api.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib/api.ts b/src/lib/api.ts index 735f027..30bf150 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -27,7 +27,7 @@ api.interceptors.response.use( try { const { data } = await axios.post( - API_ENDPOINTS.AUTH.REISSUE, + `${process.env.NEXT_PUBLIC_API_BASE_URL ?? ""}${API_ENDPOINTS.AUTH.REISSUE}`, {}, { withCredentials: true }, ); From d9549398887cf91f5aa62ac44b92d6ade195f6ba Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 03:26:26 +0900 Subject: [PATCH 086/103] =?UTF-8?q?fix:=20=EC=9D=B4=EB=A9=94=EC=9D=BC=20?= =?UTF-8?q?=EC=9C=A0=ED=9A=A8=EC=84=B1=20=EA=B2=80=EC=82=AC=EB=A5=BC=20zod?= =?UTF-8?q?=20=EB=82=B4=EC=9E=A5=20=EA=B2=80=EC=A6=9D=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EA=B5=90=EC=B2=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/schemas/signup.ts | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/schemas/signup.ts b/src/schemas/signup.ts index f642559..6e97b40 100644 --- a/src/schemas/signup.ts +++ b/src/schemas/signup.ts @@ -1,14 +1,12 @@ import { z } from "zod"; -const EMAIL_PATTERN = /^[A-Za-z0-9_.-]+@[A-Za-z0-9-]+\.[A-Za-z0-9-]+/; - export const signupSchema = z .object({ part: z.enum(["frontend", "backend"]), team: z.string().min(1), member: z.string().min(1), username: z.string().min(1), - email: z.string().regex(EMAIL_PATTERN, "이메일 형식이 올바르지 않습니다"), + email: z.email("이메일 형식이 올바르지 않습니다"), password: z.string().min(1), passwordRe: z.string().min(1), }) From 3d0cb98170dc77ac8786d6e7117410d5898e10fc Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 24 Jun 2026 15:59:47 +0900 Subject: [PATCH 087/103] =?UTF-8?q?feat:=20proxy=20->=20=EB=B9=84=EB=A1=9C?= =?UTF-8?q?=EA=B7=B8=EC=9D=B8=20=EC=8B=9C=20=ED=8A=B9=EC=A0=95=20=ED=8E=98?= =?UTF-8?q?=EC=9D=B4=EC=A7=80=20=EC=A0=91=EA=B7=BC=20=EC=A0=9C=ED=95=9C=20?= =?UTF-8?q?=EB=AA=A8=EB=8B=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/globals.css | 4 +-- src/components/LoginRequiredModal.tsx | 45 +++++++++++++++++++++++++ src/components/NavBar.tsx | 48 +++++++++++++++++++++++---- 3 files changed, 89 insertions(+), 8 deletions(-) create mode 100644 src/components/LoginRequiredModal.tsx diff --git a/src/app/globals.css b/src/app/globals.css index ae79927..9e05352 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -49,12 +49,12 @@ --text-label2--font-weight: 400; /* Caption */ - --text-caption1: 12px; + --text-caption1: 13px; --text-caption1--line-height: 135%; --text-caption1--letter-spacing: -0.02em; --text-caption1--font-weight: 400; - --text-caption2: 10px; + --text-caption2: 11px; --text-caption2--line-height: 135%; --text-caption2--letter-spacing: -0.02em; --text-caption2--font-weight: 500; diff --git a/src/components/LoginRequiredModal.tsx b/src/components/LoginRequiredModal.tsx new file mode 100644 index 0000000..ec4c94d --- /dev/null +++ b/src/components/LoginRequiredModal.tsx @@ -0,0 +1,45 @@ +"use client"; + +import { useRouter } from "next/navigation"; + +interface LoginRequiredModalProps { + onClose: () => void; +} + +export default function LoginRequiredModal({ onClose }: LoginRequiredModalProps) { + const router = useRouter(); + + const handleLogin = () => { + onClose(); + router.push("/login"); + }; + + return ( +
      +
      e.stopPropagation()} + > +

      로그인 후 이용 가능한 페이지입니다.

      + +
      + + +
      +
      +
      + ); +} diff --git a/src/components/NavBar.tsx b/src/components/NavBar.tsx index 9f3c40d..a348cb0 100644 --- a/src/components/NavBar.tsx +++ b/src/components/NavBar.tsx @@ -3,28 +3,53 @@ import Link from "next/link"; import Image from "next/image"; import { usePathname, useRouter } from "next/navigation"; -import { useState } from "react"; +import { useMemo, useState } from "react"; import { useAuthStore } from "@/store/authStore"; import { logout as logoutApi } from "@/api/auth"; +import LoginRequiredModal from "@/components/LoginRequiredModal"; interface NavItem { label: string; href: string; + protected?: boolean; } const NAV_ITEMS: NavItem[] = [ - { label: "VOTING", href: "/voting" }, - { label: "MEMBERS", href: "/members" }, + { label: "VOTING", href: "/voting", protected: true }, + { label: "MEMBERS", href: "/members", protected: true }, { label: "ABOUT US", href: "/about" }, ]; +const PROTECTED_PATHS = NAV_ITEMS.filter((i) => i.protected).map((i) => i.href); + export default function NavBar({ className }: { className?: string }) { const pathname = usePathname(); const router = useRouter(); const [open, setOpen] = useState(false); + const [showLoginModal, setShowLoginModal] = useState(false); const isLoggedIn = useAuthStore((s) => s.accessToken !== null); const clearAuth = useAuthStore((s) => s.clearAuth); + // Proxy + const guardedRouter = useMemo( + () => + new Proxy(router, { + get(target, prop, receiver) { + if (prop === "push") { + return (href: string, options?: Parameters[1]) => { + if (!isLoggedIn && PROTECTED_PATHS.some((p) => href.startsWith(p))) { + setShowLoginModal(true); + return Promise.resolve(true); + } + return target.push(href, options); + }; + } + return Reflect.get(target, prop, receiver); + }, + }), + [router, isLoggedIn], + ); + const handleLogout = async () => { try { await logoutApi(); @@ -46,6 +71,10 @@ export default function NavBar({ className }: { className?: string }) { { + e.preventDefault(); + guardedRouter.push(item.href); + }} className={ pathname === item.href ? "text-blue-500" : "text-white" } @@ -71,7 +100,7 @@ export default function NavBar({ className }: { className?: string }) {
      - {/* 모바일 헤더 */} + {/* 모바일 */}
      CEOS @@ -88,7 +117,6 @@ export default function NavBar({ className }: { className?: string }) {
      - {/* 모바일 드로어 */} {open && (
      setOpen(false)} /> @@ -105,7 +133,11 @@ export default function NavBar({ className }: { className?: string }) { setOpen(false)} + onClick={(e) => { + e.preventDefault(); + setOpen(false); + guardedRouter.push(item.href); + }} className={`text-2xl font-bold ${pathname === item.href ? "text-blue-500" : "text-white"}`} > {item.label} @@ -133,6 +165,10 @@ export default function NavBar({ className }: { className?: string }) {
      )} + + {showLoginModal && ( + setShowLoginModal(false)} /> + )}
      ); } From 7bb02f5f9143899d2da88fa949354625e777436e Mon Sep 17 00:00:00 2001 From: girimNam Date: Wed, 24 Jun 2026 16:08:58 +0900 Subject: [PATCH 088/103] =?UTF-8?q?refactor:=20=ED=88=AC=ED=91=9C=20?= =?UTF-8?q?=EC=8B=9C=20=EC=97=90=EB=9F=AC=20=EB=AA=A8=EB=8B=AC=EC=B0=BD=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/voting/demoday/page.tsx | 5 ++--- src/app/voting/leader/page.tsx | 5 ++--- src/components/ErrorModal.tsx | 31 +++++++++++++++++++++++++++++++ 3 files changed, 35 insertions(+), 6 deletions(-) create mode 100644 src/components/ErrorModal.tsx diff --git a/src/app/voting/demoday/page.tsx b/src/app/voting/demoday/page.tsx index ea6b247..a68c787 100644 --- a/src/app/voting/demoday/page.tsx +++ b/src/app/voting/demoday/page.tsx @@ -5,6 +5,7 @@ import { useRouter } from "next/navigation"; import { TEAM_NAMES, TeamName } from "@/constants/teams"; import { voteDemoDay } from "@/api/vote"; import { useAuthStore } from "@/store/authStore"; +import ErrorModal from "@/components/ErrorModal"; export default function VotingDemoday() { const [selected, setSelected] = useState(null); @@ -93,9 +94,7 @@ export default function VotingDemoday() { {voteError && ( -

      - {voteError} -

      + setVoteError(null)} /> )}
    {voteError && ( -

    - {voteError} -

    + setVoteError(null)} /> )} + + + + ); +} From 7fdbe3f38f3d2ac585fe1c3d1bb22bd7e9559b3e Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 18:17:30 +0900 Subject: [PATCH 089/103] =?UTF-8?q?fix:=20U002/U005=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EB=A5=BC=20member=20=ED=95=84=EB=93=9C=20=EC=97=90=EB=9F=AC?= =?UTF-8?q?=EB=A1=9C=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index 747eb44..efd765d 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -190,8 +190,10 @@ export default function Signup() { setError("passwordRe", { message: message ?? "비밀번호가 일치하지 않습니다." }); break; case "U002": + setError("member", { message: message ?? "선택한 후보 정보가 올바르지 않습니다." }); + break; case "U005": - setServerError(message ?? "선택한 후보 정보가 올바르지 않습니다."); + setError("member", { message: message ?? "이미 가입된 후보입니다." }); break; default: setServerError(message ?? "회원가입에 실패했습니다."); From cd96132960945f6efe5982cc7f7f3e327e822034 Mon Sep 17 00:00:00 2001 From: minsxo Date: Wed, 24 Jun 2026 19:14:40 +0900 Subject: [PATCH 090/103] =?UTF-8?q?fix:=20U001~U005=20=EB=B0=B1=EC=97=94?= =?UTF-8?q?=EB=93=9C=20=EC=97=90=EB=9F=AC=20=EA=B0=81=20=ED=95=84=EB=93=9C?= =?UTF-8?q?=20=EC=95=84=EB=9E=98=EC=97=90=20=ED=91=9C=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/signup/page.tsx | 64 +++++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 16 deletions(-) diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index efd765d..88f7305 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -85,13 +85,25 @@ export default function Signup() { const router = useRouter(); const [submitting, setSubmitting] = useState(false); const [serverError, setServerError] = useState(null); + const [apiFieldErrors, setApiFieldErrors] = useState<{ + email?: string; + username?: string; + member?: string; + passwordRe?: string; + }>({}); + const clearApiFieldError = (field: keyof typeof apiFieldErrors) => + setApiFieldErrors((prev) => { + if (!prev[field]) return prev; + const next = { ...prev }; + delete next[field]; + return next; + }); const { register, control, handleSubmit, watch, setValue, - setError, formState: { errors }, } = useForm({ resolver: zodResolver(signupSchema), @@ -148,6 +160,7 @@ export default function Signup() { const onSubmit = async (data: SignupForm) => { setServerError(null); + setApiFieldErrors({}); setSubmitting(true); try { const res = await signup({ @@ -181,19 +194,19 @@ export default function Signup() { const handleSignupError = (code?: string, message?: string) => { switch (code) { case "U003": - setError("username", { message: message ?? "이미 사용 중인 아이디입니다." }); + setApiFieldErrors({ username: message ?? "이미 사용 중인 아이디입니다." }); break; case "U004": - setError("email", { message: message ?? "이미 사용 중인 이메일입니다." }); + setApiFieldErrors({ email: message ?? "이미 사용 중인 이메일입니다." }); break; case "U001": - setError("passwordRe", { message: message ?? "비밀번호가 일치하지 않습니다." }); + setApiFieldErrors({ passwordRe: message ?? "비밀번호가 일치하지 않습니다." }); break; case "U002": - setError("member", { message: message ?? "선택한 후보 정보가 올바르지 않습니다." }); + setApiFieldErrors({ member: message ?? "선택한 후보 정보가 올바르지 않습니다." }); break; case "U005": - setError("member", { message: message ?? "이미 가입된 후보입니다." }); + setApiFieldErrors({ member: message ?? "이미 가입된 후보입니다." }); break; default: setServerError(message ?? "회원가입에 실패했습니다."); @@ -252,39 +265,55 @@ export default function Signup() { value={field.value} placeholder="이름을 선택해 주세요" options={memberOptions} - onChange={field.onChange} + onChange={(v) => { + field.onChange(v); + clearApiFieldError("member"); + }} disabled={!team} /> )} /> -
    ); } From 463220790ca2809cdf5e47cf500c85af165ab07b Mon Sep 17 00:00:00 2001 From: girimNam Date: Fri, 26 Jun 2026 00:46:39 +0900 Subject: [PATCH 092/103] =?UTF-8?q?feat:=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20a?= =?UTF-8?q?pi=20=EC=97=B0=EB=8F=99=20=EC=99=84=EB=A3=8C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/app/(auth)/login/page.tsx | 90 ++++++++++++++++++++++----- src/app/(auth)/signup/page.tsx | 24 +++---- src/components/LoginRequiredModal.tsx | 6 +- src/components/NavBar.tsx | 12 +++- src/lib/api.ts | 16 +++-- 5 files changed, 110 insertions(+), 38 deletions(-) diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx index d82f4db..f511d03 100644 --- a/src/app/(auth)/login/page.tsx +++ b/src/app/(auth)/login/page.tsx @@ -4,11 +4,12 @@ import Link from "next/link"; import { useForm } from "react-hook-form"; import { zodResolver } from "@hookform/resolvers/zod"; import { loginSchema, LoginForm } from "@/schemas/login"; -import { useRouter } from "next/navigation"; +import { useRouter, useSearchParams } from "next/navigation"; import { useState } from "react"; import { login } from "@/api/auth"; import { useAuthStore } from "@/store/authStore"; + export default function Login() { const { register, @@ -25,29 +26,67 @@ export default function Login() { }); const router = useRouter(); + const searchParams = useSearchParams(); const setAuth = useAuthStore((s) => s.setAuth); const username = watch("username"); const password = watch("password"); - const [serverError, setServerError] = useState(""); + + const [apiFieldErrors, setApiFieldErrors] = useState<{ + username?: string; + password?: string; + }>({}); + const [serverError, setServerError] = useState(null); const [isLoading, setIsLoading] = useState(false); + const clearApiFieldError = (field: keyof typeof apiFieldErrors) => + setApiFieldErrors((prev) => { + if (!prev[field]) return prev; + const next = { ...prev }; + delete next[field]; + return next; + }); + + const handleLoginError = (status?: number, code?: string) => { + if (code === "A001") { + setApiFieldErrors({ username: "존재하지 않는 아이디입니다." }); + } else if (code === "A002") { + setApiFieldErrors({ password: "비밀번호가 올바르지 않습니다." }); + } else if (status === 400 || code === "C001") { + setServerError("필수 항목을 모두 입력해 주세요."); + } else if (status === 500 || code === "C003") { + setServerError("서버 내부 오류가 발생했습니다. 잠시 후 다시 시도해주세요."); + } else { + setServerError("로그인 중 오류가 발생했습니다. 다시 시도해주세요."); + } + }; + const onSubmit = async (data: LoginForm) => { - setServerError(""); + setServerError(null); + setApiFieldErrors({}); setIsLoading(true); try { const res = await login({ username: data.username, password: data.password }); setAuth(res.data!.accessToken, res.data!.user); - router.push("/members"); + console.log("로그인 성공", res.data!.user); + const redirect = searchParams.get("redirect"); + router.push(redirect ?? "/members"); + } catch (err: unknown) { if (err && typeof err === "object" && "response" in err) { - const status = (err as { response: { status: number } }).response.status; - if (status === 401) { - setServerError("아이디 또는 비밀번호가 올바르지 않습니다."); - } else { - setServerError("로그인 중 오류가 발생했습니다. 다시 시도해주세요."); - } + const axiosErr = err as { + response: { status: number; data?: { error?: { code?: string; message?: string } } }; + }; + + const { status, data: resData } = axiosErr.response; + console.error("로그인 실패 status:", status, ", code:", resData?.error?.code, ", message:", resData?.error?.message); + handleLoginError(status, resData?.error?.code); + + } else { + console.error("알 수 없는 오류:", err); + setServerError("로그인 중 오류가 발생했습니다. 다시 시도해주세요."); } + } finally { setIsLoading(false); } @@ -57,31 +96,48 @@ export default function Login() {

    LOGIN

    -
    + { + console.error("로그인 유효성 오류:", fieldErrors); + })} + className="flex flex-col gap-3.5" + > {serverError && ( diff --git a/src/app/(auth)/signup/page.tsx b/src/app/(auth)/signup/page.tsx index f531da4..3293592 100644 --- a/src/app/(auth)/signup/page.tsx +++ b/src/app/(auth)/signup/page.tsx @@ -215,7 +215,7 @@ export default function Signup() { }; return ( -
    +

    SIGNUP

    @@ -229,7 +229,7 @@ export default function Signup() { key={p} type="button" onClick={() => handlePartChange(p)} - className={`w-1/2 md:w-[17.1875rem] h-[3.1875rem] border border-black text-label2 cursor-pointer transition-colors ${rounded} ${ + className={`w-1/2 md:w-68.75 h-12.75 border border-black text-label2 cursor-pointer transition-colors ${rounded} ${ selected ? "bg-black text-white" : "bg-white text-black" }`} > @@ -276,12 +276,12 @@ export default function Signup() { /> -

    +

    {apiFieldErrors.member ?? " "}

    -

    +

    {apiFieldErrors.username ?? " "}

    -

    +

    {apiFieldErrors.email ?? (email.length > 0 && errors.email ? errors.email.message : " ")}