diff --git a/.eslintrc.json b/.eslintrc.json
index bffb357..f64128d 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -1,3 +1,6 @@
{
- "extends": "next/core-web-vitals"
+ "extends": [
+ "next/core-web-vitals",
+ "prettier"
+ ]
}
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
index af5aee2..2ee9bff 100644
--- a/.github/workflows/deploy.yml
+++ b/.github/workflows/deploy.yml
@@ -17,16 +17,16 @@ jobs:
runs-on: ubuntu-latest
env:
NEXT_PUBLIC_GOOGLE_REDIRECT_CLIENT_ID: ${{ secrets.NEXT_PUBLIC_GOOGLE_REDIRECT_CLIENT_ID }}
- NEXT_PUBLIC_GOOGLE_REDIRECT_URI: ${{ secrets.NEXT_PUBLIC_GOOGLE_REDIRECT_URI }}
+ NEXT_PUBLIC_GOOGLE_MAPS_API_KEY: ${{ secrets.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY }}
steps:
- name: Checkout source code
uses: actions/checkout@v4
- - name: Use Node.js 20
+ - name: Use Node.js 22
uses: actions/setup-node@v4
with:
- node-version: '20'
+ node-version: '22'
cache: 'yarn'
- name: Set env for develop
@@ -35,7 +35,7 @@ jobs:
echo "TARGET_BUCKET=${{ secrets.AWS_S3_BUCKET_DEV }}" >> $GITHUB_ENV
echo "NEXT_PUBLIC_APP_ENV=dev" >> $GITHUB_ENV
echo "NEXT_PUBLIC_BASE_API_URL=${{ secrets.NEXT_PUBLIC_BASE_API_URL_DEV }}" >> $GITHUB_ENV
- echo "NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=${{ secrets.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY }}" >> $GITHUB_ENV
+ echo "NEXT_PUBLIC_GOOGLE_REDIRECT_URI=${{ secrets.NEXT_PUBLIC_GOOGLE_REDIRECT_URI_DEV }}" >> $GITHUB_ENV
- name: Set env for main
if: github.ref == 'refs/heads/master'
@@ -43,7 +43,7 @@ jobs:
echo "TARGET_BUCKET=${{ secrets.AWS_S3_BUCKET }}" >> $GITHUB_ENV
echo "NEXT_PUBLIC_APP_ENV=production" >> $GITHUB_ENV
echo "NEXT_PUBLIC_BASE_API_URL=${{ secrets.NEXT_PUBLIC_BASE_API_URL }}" >> $GITHUB_ENV
- echo "NEXT_PUBLIC_GOOGLE_MAPS_API_KEY=${{ secrets.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY }}" >> $GITHUB_ENV
+ echo "NEXT_PUBLIC_GOOGLE_REDIRECT_URI=${{ secrets.NEXT_PUBLIC_GOOGLE_REDIRECT_URI }}" >> $GITHUB_ENV
- name: Install dependencies
run: yarn install --frozen-lockfile
@@ -60,19 +60,19 @@ jobs:
# (선택) 캐시 헤더 전략: 해시된 자산은 장기 캐시, HTML은 no-cache
# 프로젝트에 맞게 include/exclude 패턴 조정
- - name: Upload static assets (long cache)
+ - name: Sync static assets (long cache, delete removed)
run: |
- aws s3 cp ./out s3://$TARGET_BUCKET \
- --recursive \
+ aws s3 sync ./out s3://$TARGET_BUCKET \
+ --delete \
--exclude "*" \
--include "_next/**" --include "static/**" --include "assets/**" \
--cache-control "public, max-age=31536000, immutable" \
--metadata-directive REPLACE
- - name: Upload html (no cache)
+ - name: Sync html (no cache, delete removed)
run: |
- aws s3 cp ./out s3://$TARGET_BUCKET \
- --recursive \
- --exclude "_next/*" --exclude "static/*" --exclude "assets/*" \
+ aws s3 sync ./out s3://$TARGET_BUCKET \
+ --delete \
+ --exclude "_next/**" --exclude "static/**" --exclude "assets/**" \
--cache-control "no-cache" \
--metadata-directive REPLACE
# (단순화 원하면 기존 sync 한 줄 유지 가능)
diff --git a/.gitignore b/.gitignore
index 9044c75..a099383 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,4 +40,4 @@ yarn-error.log*
next-env.d.ts
# ide files
-.idea/*
\ No newline at end of file
+.idea
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..5c00bb7
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,6 @@
+node_modules
+.next
+out
+build
+coverage
+dist
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..cca78ca
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,8 @@
+{
+ "singleQuote": true,
+ "semi": false,
+ "tabWidth": 2,
+ "trailingComma": "none",
+ "printWidth": 100,
+ "endOfLine": "lf"
+}
diff --git a/Dockerfile b/Dockerfile
index d6c8617..4521bc4 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -1,5 +1,5 @@
# 1단계: 빌드 환경
-FROM node:18 AS builder
+FROM node:22 AS builder
WORKDIR /app
@@ -26,7 +26,7 @@ RUN ls -la /app/.next || echo ".next 디렉토리가 없습니다"
RUN ls -la /app/out || echo "out 디렉토리가 없습니다"
# 2단계: 실행 환경
-FROM node:18
+FROM node:22
WORKDIR /app
@@ -51,4 +51,4 @@ RUN ls -la /app/.next || echo ".next 디렉토리가 없습니다"
EXPOSE 3000
# Next.js 서버 시작
-CMD ["npm", "run", "start"]
\ No newline at end of file
+CMD ["npm", "run", "start"]
diff --git a/package-lock.json b/package-lock.json
index c5e0d69..d3ce40c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,45 +9,52 @@
"version": "0.1.0",
"dependencies": {
"@heroicons/react": "^2.2.0",
- "@heroui/scroll-shadow": "^2.3.6",
- "@heroui/system": "^2.4.7",
- "@heroui/theme": "^2.4.6",
+ "@heroui/scroll-shadow": "^2.3.19",
+ "@heroui/system": "^2.4.25",
+ "@heroui/theme": "^2.4.25",
"@nextui-org/react": "^2.6.11",
- "@react-google-maps/api": "^2.20.7",
+ "@react-google-maps/api": "^2.20.8",
"@splidejs/react-splide": "^0.7.12",
"@splidejs/splide": "^4.1.4",
"@splidejs/splide-extension-auto-scroll": "^0.5.3",
"@studio-freight/lenis": "^1.0.42",
- "axios": "^1.7.9",
- "framer-motion": "^11.16.1",
- "gsap": "^3.12.7",
+ "axios": "1.13.2",
+ "clsx": "^2.1.1",
+ "framer-motion": "^12.24.7",
+ "gsap": "^3.14.2",
"hangul-js": "^0.2.6",
- "lucide-react": "^0.507.0",
- "next": ">=15.2.3",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-error-boundary": "^6.0.0",
+ "lucide-react": "^0.562.0",
+ "next": "15.5.9",
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
+ "react-error-boundary": "^6.0.2",
"react-icons": "^5.4.0",
"react-markdown": "^10.1.0",
- "recharts": "^3.1.2",
+ "recharts": "^3.6.0",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
+ "tailwind-merge": "^3.4.0",
"type-hangul": "^0.2.4"
},
"devDependencies": {
+ "@tailwindcss/postcss": "^4.1.18",
"@types/node": "22.15.21",
- "@types/react": "19.1.5",
+ "@types/react": "^18.2.0",
"eslint": "^8",
- "eslint-config-next": "15.0.2",
+ "eslint-config-next": "15.5.9",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-react": "^7.37.5",
"postcss": "^8",
- "tailwindcss": "^3.4.1",
- "typescript": "^5.8.3"
+ "prettier": "^3.5.1",
+ "tailwindcss": "^4.1.18",
+ "typescript": "^5.9.3"
}
},
"node_modules/@alloc/quick-lru": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
"integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=10"
@@ -57,9 +64,9 @@
}
},
"node_modules/@babel/runtime": {
- "version": "7.27.1",
- "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz",
- "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==",
+ "version": "7.28.4",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz",
+ "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==",
"license": "MIT",
"engines": {
"node": ">=6.9.0"
@@ -78,9 +85,9 @@
}
},
"node_modules/@emnapi/runtime": {
- "version": "1.4.3",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
- "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
+ "version": "1.8.1",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz",
+ "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==",
"license": "MIT",
"optional": true,
"dependencies": {
@@ -99,9 +106,9 @@
}
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.7.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz",
- "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==",
+ "version": "4.9.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz",
+ "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -118,9 +125,9 @@
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.12.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
- "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
"engines": {
@@ -238,65 +245,62 @@
}
},
"node_modules/@heroui/react-rsc-utils": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/@heroui/react-rsc-utils/-/react-rsc-utils-2.1.7.tgz",
- "integrity": "sha512-NYKKOLs+KHA8v0+PxkkhVXxTD0WNvC4QMlMjUVshzpWhjnOHIrtXjAtqO6XezWmiKNKY76FAjnMZP+Be5+j5uw==",
+ "version": "2.1.9",
+ "resolved": "https://registry.npmjs.org/@heroui/react-rsc-utils/-/react-rsc-utils-2.1.9.tgz",
+ "integrity": "sha512-e77OEjNCmQxE9/pnLDDb93qWkX58/CcgIqdNAczT/zUP+a48NxGq2A2WRimvc1uviwaNL2StriE2DmyZPyYW7Q==",
"license": "MIT",
"peerDependencies": {
"react": ">=18 || >=19.0.0-rc.0"
}
},
"node_modules/@heroui/react-utils": {
- "version": "2.1.10",
- "resolved": "https://registry.npmjs.org/@heroui/react-utils/-/react-utils-2.1.10.tgz",
- "integrity": "sha512-Wj3BSQnNFrDzDnN44vYEwTScMpdbylbZwO8UxIY02AoQCBD5QW7Wf0r2FVlrsrjPjMOVeogwlVvCBYvZz5hHnQ==",
+ "version": "2.1.14",
+ "resolved": "https://registry.npmjs.org/@heroui/react-utils/-/react-utils-2.1.14.tgz",
+ "integrity": "sha512-hhKklYKy9sRH52C9A8P0jWQ79W4MkIvOnKBIuxEMHhigjfracy0o0lMnAUdEsJni4oZKVJYqNGdQl+UVgcmeDA==",
"license": "MIT",
"dependencies": {
- "@heroui/react-rsc-utils": "2.1.7",
- "@heroui/shared-utils": "2.1.9"
+ "@heroui/react-rsc-utils": "2.1.9",
+ "@heroui/shared-utils": "2.1.12"
},
"peerDependencies": {
"react": ">=18 || >=19.0.0-rc.0"
}
},
"node_modules/@heroui/scroll-shadow": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@heroui/scroll-shadow/-/scroll-shadow-2.3.13.tgz",
- "integrity": "sha512-RfYfVewf6UR4vr4sIPI2NaNoyK5lLgJwdWNGufE1Km7INelXf3BVdVKLW/Qlq/cES+B4TV3gq5Nto8aen3R1Sg==",
+ "version": "2.3.19",
+ "resolved": "https://registry.npmjs.org/@heroui/scroll-shadow/-/scroll-shadow-2.3.19.tgz",
+ "integrity": "sha512-y5mdBlhiITVrFnQTDqEphYj7p5pHqoFSFtVuRRvl9wUec2lMxEpD85uMGsfL8OgQTKIAqGh2s6M360+VJm7ajQ==",
"license": "MIT",
"dependencies": {
- "@heroui/react-utils": "2.1.10",
- "@heroui/shared-utils": "2.1.9",
- "@heroui/use-data-scroll-overflow": "2.2.10"
+ "@heroui/react-utils": "2.1.14",
+ "@heroui/shared-utils": "2.1.12",
+ "@heroui/use-data-scroll-overflow": "2.2.13"
},
"peerDependencies": {
- "@heroui/system": ">=2.4.7",
- "@heroui/theme": ">=2.4.6",
+ "@heroui/system": ">=2.4.18",
+ "@heroui/theme": ">=2.4.23",
"react": ">=18 || >=19.0.0-rc.0",
"react-dom": ">=18 || >=19.0.0-rc.0"
}
},
"node_modules/@heroui/shared-utils": {
- "version": "2.1.9",
- "resolved": "https://registry.npmjs.org/@heroui/shared-utils/-/shared-utils-2.1.9.tgz",
- "integrity": "sha512-mM/Ep914cYMbw3T/b6+6loYhuNfzDaph76mzw/oIS05gw1Dhp9luCziSiIhqDGgzYck2d74oWTZlahyCsxf47w==",
+ "version": "2.1.12",
+ "resolved": "https://registry.npmjs.org/@heroui/shared-utils/-/shared-utils-2.1.12.tgz",
+ "integrity": "sha512-0iCnxVAkIPtrHQo26Qa5g0UTqMTpugTbClNOrEPsrQuyRAq7Syux998cPwGlneTfB5E5xcU3LiEdA9GUyeK2cQ==",
"hasInstallScript": true,
"license": "MIT"
},
"node_modules/@heroui/system": {
- "version": "2.4.15",
- "resolved": "https://registry.npmjs.org/@heroui/system/-/system-2.4.15.tgz",
- "integrity": "sha512-+QUHscs2RTk5yOFEQXNlQa478P7PTD02ZGP/RTNCviR4E9ZTUifdjfsKA7D4L79S7L8Mkvbz5E2Ruz2ZF0R/IA==",
+ "version": "2.4.25",
+ "resolved": "https://registry.npmjs.org/@heroui/system/-/system-2.4.25.tgz",
+ "integrity": "sha512-F6UUoGTQ+Qas5wYkCzLjXE7u74Z9ygO0u0+dkTW7zCaY7ds65CcmvZ/ahKz2ES3Tk6TNks1MJSyaQ9rFLs8AqA==",
"license": "MIT",
"dependencies": {
- "@heroui/react-utils": "2.1.10",
- "@heroui/system-rsc": "2.3.13",
- "@internationalized/date": "3.8.0",
- "@react-aria/i18n": "3.12.8",
- "@react-aria/overlays": "3.27.0",
- "@react-aria/utils": "3.28.2",
- "@react-stately/utils": "3.10.6",
- "@react-types/datepicker": "3.12.0"
+ "@heroui/react-utils": "2.1.14",
+ "@heroui/system-rsc": "2.3.21",
+ "@react-aria/i18n": "3.12.14",
+ "@react-aria/overlays": "3.31.0",
+ "@react-aria/utils": "3.32.0"
},
"peerDependencies": {
"framer-motion": ">=11.5.6 || >=12.0.0-alpha.1",
@@ -305,45 +309,62 @@
}
},
"node_modules/@heroui/system-rsc": {
- "version": "2.3.13",
- "resolved": "https://registry.npmjs.org/@heroui/system-rsc/-/system-rsc-2.3.13.tgz",
- "integrity": "sha512-zLBrDKCoM4o039t3JdfYZAOlHmn4RzI6gxU+Tw8XJIfvUzpGSvR2seY2XJBbKOonmTpILlnw16ZvHF+KG+nN0w==",
+ "version": "2.3.21",
+ "resolved": "https://registry.npmjs.org/@heroui/system-rsc/-/system-rsc-2.3.21.tgz",
+ "integrity": "sha512-icB7njbNgkI3dcfZhY5LP7VFspaVgWL1lcg9Q7uJMAaj6gGFqqSSnHkSMwpR9AGLxVRKTHey0TUx8CeZDe8XDw==",
"license": "MIT",
"dependencies": {
- "@react-types/shared": "3.29.0",
- "clsx": "^1.2.1"
+ "@react-types/shared": "3.32.1"
},
"peerDependencies": {
- "@heroui/theme": ">=2.4.6",
+ "@heroui/theme": ">=2.4.23",
"react": ">=18 || >=19.0.0-rc.0"
}
},
"node_modules/@heroui/theme": {
- "version": "2.4.15",
- "resolved": "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.15.tgz",
- "integrity": "sha512-cP1N9Rqj5wzsKLpEzNdJQRjX2g9AuCZbRNaIuIGnztqmmGtP3Yykt1RzeQ4ukCdSDjk/PmV8XneTu8OC8Cs8HA==",
+ "version": "2.4.25",
+ "resolved": "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.25.tgz",
+ "integrity": "sha512-nTptYhO1V9rMoh9SJDnMfaSmFuoXvbem1UuwgHcraRtqy/TIVBPqv26JEGzSoUCL194TDGOJpqrpMuab/PdXcw==",
"license": "MIT",
"dependencies": {
- "@heroui/shared-utils": "2.1.9",
- "clsx": "^1.2.1",
+ "@heroui/shared-utils": "2.1.12",
"color": "^4.2.3",
"color2k": "^2.0.3",
"deepmerge": "4.3.1",
"flat": "^5.0.2",
- "tailwind-merge": "2.5.4",
- "tailwind-variants": "0.3.0"
+ "tailwind-merge": "3.4.0",
+ "tailwind-variants": "3.2.2"
},
"peerDependencies": {
- "tailwindcss": ">=3.4.0"
+ "tailwindcss": ">=4.0.0"
+ }
+ },
+ "node_modules/@heroui/theme/node_modules/tailwind-variants": {
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz",
+ "integrity": "sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=16.x",
+ "pnpm": ">=7.x"
+ },
+ "peerDependencies": {
+ "tailwind-merge": ">=3.0.0",
+ "tailwindcss": "*"
+ },
+ "peerDependenciesMeta": {
+ "tailwind-merge": {
+ "optional": true
+ }
}
},
"node_modules/@heroui/use-data-scroll-overflow": {
- "version": "2.2.10",
- "resolved": "https://registry.npmjs.org/@heroui/use-data-scroll-overflow/-/use-data-scroll-overflow-2.2.10.tgz",
- "integrity": "sha512-Lza9S7ZWhY3PliahSgDRubrpeT7gnySH67GSTrGQMzYggTDMo2I1Pky7ZaHUnHHYB9Y7WHryB26ayWBOgRtZUQ==",
+ "version": "2.2.13",
+ "resolved": "https://registry.npmjs.org/@heroui/use-data-scroll-overflow/-/use-data-scroll-overflow-2.2.13.tgz",
+ "integrity": "sha512-zboLXO1pgYdzMUahDcVt5jf+l1jAQ/D9dFqr7AxWLfn6tn7/EgY0f6xIrgWDgJnM0U3hKxVeY13pAeB4AFTqTw==",
"license": "MIT",
"dependencies": {
- "@heroui/shared-utils": "2.1.9"
+ "@heroui/shared-utils": "2.1.12"
},
"peerDependencies": {
"react": ">=18 || >=19.0.0-rc.0"
@@ -387,10 +408,20 @@
"dev": true,
"license": "BSD-3-Clause"
},
+ "node_modules/@img/colour": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz",
+ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==",
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=18"
+ }
+ },
"node_modules/@img/sharp-darwin-arm64": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz",
- "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
"cpu": [
"arm64"
],
@@ -406,13 +437,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.1.0"
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-darwin-x64": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz",
- "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
"cpu": [
"x64"
],
@@ -428,13 +459,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.1.0"
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
}
},
"node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz",
- "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
"cpu": [
"arm64"
],
@@ -448,9 +479,9 @@
}
},
"node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz",
- "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
"cpu": [
"x64"
],
@@ -464,9 +495,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz",
- "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
"cpu": [
"arm"
],
@@ -480,9 +511,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz",
- "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz",
+ "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==",
"cpu": [
"arm64"
],
@@ -496,9 +527,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-ppc64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz",
- "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz",
+ "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==",
"cpu": [
"ppc64"
],
@@ -511,10 +542,26 @@
"url": "https://opencollective.com/libvips"
}
},
+ "node_modules/@img/sharp-libvips-linux-riscv64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz",
+ "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
"node_modules/@img/sharp-libvips-linux-s390x": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz",
- "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz",
+ "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==",
"cpu": [
"s390x"
],
@@ -528,9 +575,9 @@
}
},
"node_modules/@img/sharp-libvips-linux-x64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz",
- "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz",
+ "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==",
"cpu": [
"x64"
],
@@ -544,9 +591,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-arm64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz",
- "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz",
+ "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==",
"cpu": [
"arm64"
],
@@ -560,9 +607,9 @@
}
},
"node_modules/@img/sharp-libvips-linuxmusl-x64": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz",
- "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==",
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz",
+ "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==",
"cpu": [
"x64"
],
@@ -576,9 +623,9 @@
}
},
"node_modules/@img/sharp-linux-arm": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz",
- "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz",
+ "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==",
"cpu": [
"arm"
],
@@ -594,13 +641,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm": "1.1.0"
+ "@img/sharp-libvips-linux-arm": "1.2.4"
}
},
"node_modules/@img/sharp-linux-arm64": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz",
- "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz",
+ "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==",
"cpu": [
"arm64"
],
@@ -616,13 +663,57 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-arm64": "1.1.0"
+ "@img/sharp-libvips-linux-arm64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-ppc64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz",
+ "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-ppc64": "1.2.4"
+ }
+ },
+ "node_modules/@img/sharp-linux-riscv64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz",
+ "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-riscv64": "1.2.4"
}
},
"node_modules/@img/sharp-linux-s390x": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz",
- "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz",
+ "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==",
"cpu": [
"s390x"
],
@@ -638,13 +729,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-s390x": "1.1.0"
+ "@img/sharp-libvips-linux-s390x": "1.2.4"
}
},
"node_modules/@img/sharp-linux-x64": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz",
- "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
"cpu": [
"x64"
],
@@ -660,13 +751,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.1.0"
+ "@img/sharp-libvips-linux-x64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz",
- "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
"cpu": [
"arm64"
],
@@ -682,13 +773,13 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.1.0"
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
}
},
"node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz",
- "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
"cpu": [
"x64"
],
@@ -704,21 +795,40 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.1.0"
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
}
},
"node_modules/@img/sharp-wasm32": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz",
- "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
"cpu": [
"wasm32"
],
"license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
"dependencies": {
- "@emnapi/runtime": "^1.4.0"
+ "@emnapi/runtime": "^1.7.0"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
@@ -727,9 +837,9 @@
}
},
"node_modules/@img/sharp-win32-ia32": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz",
- "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
"cpu": [
"ia32"
],
@@ -746,9 +856,9 @@
}
},
"node_modules/@img/sharp-win32-x64": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz",
- "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
"cpu": [
"x64"
],
@@ -765,18 +875,18 @@
}
},
"node_modules/@internationalized/date": {
- "version": "3.8.0",
- "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.8.0.tgz",
- "integrity": "sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==",
+ "version": "3.10.1",
+ "resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.10.1.tgz",
+ "integrity": "sha512-oJrXtQiAXLvT9clCf1K4kxp3eKsQhIaZqxEyowkBcsvZDdZkbWrVmnGknxs5flTD0VGsxrxKgBCZty1EzoiMzA==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
}
},
"node_modules/@internationalized/message": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.7.tgz",
- "integrity": "sha512-gLQlhEW4iO7DEFPf/U7IrIdA3UyLGS0opeqouaFwlMObLUzwexRjbygONHDVbC9G9oFLXsLyGKYkJwqXw/QADg==",
+ "version": "3.1.8",
+ "resolved": "https://registry.npmjs.org/@internationalized/message/-/message-3.1.8.tgz",
+ "integrity": "sha512-Rwk3j/TlYZhn3HQ6PyXUV0XP9Uv42jqZGNegt0BXlxjE6G3+LwHjbQZAGHhCnCPdaA6Tvd3ma/7QzLlLkJxAWA==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0",
@@ -784,18 +894,18 @@
}
},
"node_modules/@internationalized/number": {
- "version": "3.6.1",
- "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.1.tgz",
- "integrity": "sha512-UVsb4bCwbL944E0SX50CHFtWEeZ2uB5VozZ5yDXJdq6iPZsZO5p+bjVMZh2GxHf4Bs/7xtDCcPwEa2NU9DaG/g==",
+ "version": "3.6.5",
+ "resolved": "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz",
+ "integrity": "sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
}
},
"node_modules/@internationalized/string": {
- "version": "3.2.6",
- "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.6.tgz",
- "integrity": "sha512-LR2lnM4urJta5/wYJVV7m8qk5DrMZmLRTuFhbQO5b9/sKLHgty6unQy1Li4+Su2DWydmB4aZdS5uxBRXIq2aAw==",
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/@internationalized/string/-/string-3.2.7.tgz",
+ "integrity": "sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -805,6 +915,7 @@
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
"integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"string-width": "^5.1.2",
@@ -819,9 +930,10 @@
}
},
"node_modules/@isaacs/cliui/node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -831,9 +943,10 @@
}
},
"node_modules/@isaacs/cliui/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
@@ -846,47 +959,49 @@
}
},
"node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.8",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
- "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "@jridgewell/set-array": "^1.2.1",
- "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
"@jridgewell/trace-mapping": "^0.3.24"
- },
- "engines": {
- "node": ">=6.0.0"
}
},
"node_modules/@jridgewell/resolve-uri": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
"integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/set-array": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
- "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=6.0.0"
}
},
"node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
- "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
"license": "MIT"
},
"node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.25",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
- "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@jridgewell/resolve-uri": "^3.1.0",
@@ -907,15 +1022,15 @@
}
},
"node_modules/@next/env": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.2.tgz",
- "integrity": "sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==",
+ "version": "15.5.9",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.5.9.tgz",
+ "integrity": "sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==",
"license": "MIT"
},
"node_modules/@next/eslint-plugin-next": {
- "version": "15.0.2",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.2.tgz",
- "integrity": "sha512-R9Jc7T6Ge0txjmqpPwqD8vx6onQjynO9JT73ArCYiYPvSrwYXepH/UY/WdKDY8JPWJl72sAE4iGMHPeQ5xdEWg==",
+ "version": "15.5.9",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.9.tgz",
+ "integrity": "sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -923,9 +1038,9 @@
}
},
"node_modules/@next/swc-darwin-arm64": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.2.tgz",
- "integrity": "sha512-2DR6kY/OGcokbnCsjHpNeQblqCZ85/1j6njYSkzRdpLn5At7OkSdmk7WyAmB9G0k25+VgqVZ/u356OSoQZ3z0g==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz",
+ "integrity": "sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==",
"cpu": [
"arm64"
],
@@ -939,9 +1054,9 @@
}
},
"node_modules/@next/swc-darwin-x64": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.2.tgz",
- "integrity": "sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz",
+ "integrity": "sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==",
"cpu": [
"x64"
],
@@ -955,9 +1070,9 @@
}
},
"node_modules/@next/swc-linux-arm64-gnu": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.2.tgz",
- "integrity": "sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz",
+ "integrity": "sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==",
"cpu": [
"arm64"
],
@@ -971,9 +1086,9 @@
}
},
"node_modules/@next/swc-linux-arm64-musl": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.2.tgz",
- "integrity": "sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz",
+ "integrity": "sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==",
"cpu": [
"arm64"
],
@@ -987,9 +1102,9 @@
}
},
"node_modules/@next/swc-linux-x64-gnu": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.2.tgz",
- "integrity": "sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz",
+ "integrity": "sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==",
"cpu": [
"x64"
],
@@ -1003,9 +1118,9 @@
}
},
"node_modules/@next/swc-linux-x64-musl": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.2.tgz",
- "integrity": "sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz",
+ "integrity": "sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==",
"cpu": [
"x64"
],
@@ -1019,9 +1134,9 @@
}
},
"node_modules/@next/swc-win32-arm64-msvc": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.2.tgz",
- "integrity": "sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz",
+ "integrity": "sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==",
"cpu": [
"arm64"
],
@@ -1035,9 +1150,9 @@
}
},
"node_modules/@next/swc-win32-x64-msvc": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.2.tgz",
- "integrity": "sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA==",
+ "version": "15.5.7",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz",
+ "integrity": "sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==",
"cpu": [
"x64"
],
@@ -1106,15 +1221,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/accordion/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/alert": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/@nextui-org/alert/-/alert-2.2.9.tgz",
@@ -1164,15 +1270,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/alert/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/aria-utils": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@nextui-org/aria-utils/-/aria-utils-2.2.7.tgz",
@@ -1218,15 +1315,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/aria-utils/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/autocomplete": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@nextui-org/autocomplete/-/autocomplete-2.3.9.tgz",
@@ -1309,15 +1397,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/autocomplete/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/avatar": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@nextui-org/avatar/-/avatar-2.2.6.tgz",
@@ -1355,15 +1434,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/avatar/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/badge": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@nextui-org/badge/-/badge-2.2.5.tgz",
@@ -1429,15 +1499,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/breadcrumbs/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/button": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/@nextui-org/button/-/button-2.2.9.tgz",
@@ -1490,15 +1551,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/button/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/calendar": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/@nextui-org/calendar/-/calendar-2.2.9.tgz",
@@ -1601,15 +1653,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/calendar/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/card": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/@nextui-org/card/-/card-2.2.9.tgz",
@@ -1660,15 +1703,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/card/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/checkbox": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/@nextui-org/checkbox/-/checkbox-2.3.8.tgz",
@@ -1723,15 +1757,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/checkbox/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/chip": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@nextui-org/chip/-/chip-2.2.6.tgz",
@@ -1770,15 +1795,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/chip/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/code": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@nextui-org/code/-/code-2.2.6.tgz",
@@ -1889,15 +1905,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/date-input/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/date-picker": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@nextui-org/date-picker/-/date-picker-2.3.9.tgz",
@@ -2012,15 +2019,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/date-picker/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/divider": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@nextui-org/divider/-/divider-2.2.5.tgz",
@@ -2118,15 +2116,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/dropdown/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/form": {
"version": "2.1.8",
"resolved": "https://registry.npmjs.org/@nextui-org/form/-/form-2.1.8.tgz",
@@ -2175,15 +2164,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/form/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/framer-utils": {
"version": "2.1.6",
"resolved": "https://registry.npmjs.org/@nextui-org/framer-utils/-/framer-utils-2.1.6.tgz",
@@ -2299,15 +2279,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/input-otp/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/input/node_modules/@react-aria/utils": {
"version": "3.26.0",
"resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.26.0.tgz",
@@ -2345,15 +2316,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/input/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/kbd": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@nextui-org/kbd/-/kbd-2.2.6.tgz",
@@ -2388,15 +2350,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/kbd/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/link": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@nextui-org/link/-/link-2.2.7.tgz",
@@ -2436,15 +2389,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/link/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/listbox": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@nextui-org/listbox/-/listbox-2.3.9.tgz",
@@ -2498,15 +2442,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/listbox/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/menu": {
"version": "2.2.9",
"resolved": "https://registry.npmjs.org/@nextui-org/menu/-/menu-2.2.9.tgz",
@@ -2560,15 +2495,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/menu/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/modal": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@nextui-org/modal/-/modal-2.2.7.tgz",
@@ -2640,15 +2566,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/modal/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/navbar": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/@nextui-org/navbar/-/navbar-2.2.8.tgz",
@@ -2728,15 +2645,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/navbar/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/pagination": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/@nextui-org/pagination/-/pagination-2.2.8.tgz",
@@ -2797,15 +2705,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/pagination/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/popover": {
"version": "2.3.9",
"resolved": "https://registry.npmjs.org/@nextui-org/popover/-/popover-2.3.9.tgz",
@@ -2877,15 +2776,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/popover/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/progress": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@nextui-org/progress/-/progress-2.2.6.tgz",
@@ -2943,15 +2833,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/progress/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/radio": {
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/@nextui-org/radio/-/radio-2.3.8.tgz",
@@ -3003,15 +2884,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/radio/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/react": {
"version": "2.6.11",
"resolved": "https://registry.npmjs.org/@nextui-org/react/-/react-2.6.11.tgz",
@@ -3193,15 +3065,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/select/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/shared-icons": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@nextui-org/shared-icons/-/shared-icons-2.1.1.tgz",
@@ -3294,15 +3157,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/slider/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/snippet": {
"version": "2.2.10",
"resolved": "https://registry.npmjs.org/@nextui-org/snippet/-/snippet-2.2.10.tgz",
@@ -3343,15 +3197,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/snippet/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/spacer": {
"version": "2.2.6",
"resolved": "https://registry.npmjs.org/@nextui-org/spacer/-/spacer-2.2.6.tgz",
@@ -3436,15 +3281,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/switch/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/system": {
"version": "2.4.6",
"resolved": "https://registry.npmjs.org/@nextui-org/system/-/system-2.4.6.tgz",
@@ -3489,6 +3325,15 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
+ "node_modules/@nextui-org/system-rsc/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/@nextui-org/system/node_modules/@internationalized/date": {
"version": "3.6.0",
"resolved": "https://registry.npmjs.org/@internationalized/date/-/date-3.6.0.tgz",
@@ -3583,15 +3428,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/system/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/table": {
"version": "2.2.8",
"resolved": "https://registry.npmjs.org/@nextui-org/table/-/table-2.2.8.tgz",
@@ -3637,15 +3473,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/table/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/tabs": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/@nextui-org/tabs/-/tabs-2.2.7.tgz",
@@ -3701,15 +3528,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/tabs/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/theme": {
"version": "2.4.5",
"resolved": "https://registry.npmjs.org/@nextui-org/theme/-/theme-2.4.5.tgz",
@@ -3729,6 +3547,25 @@
"tailwindcss": ">=3.4.0"
}
},
+ "node_modules/@nextui-org/theme/node_modules/clsx": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
+ "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/@nextui-org/theme/node_modules/tailwind-merge": {
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz",
+ "integrity": "sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
"node_modules/@nextui-org/theme/node_modules/tailwind-variants": {
"version": "0.1.20",
"resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.1.20.tgz",
@@ -3823,15 +3660,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/tooltip/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-aria-accordion": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@nextui-org/use-aria-accordion/-/use-aria-accordion-2.2.2.tgz",
@@ -3875,15 +3703,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/use-aria-accordion/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-aria-button": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@nextui-org/use-aria-button/-/use-aria-button-2.2.4.tgz",
@@ -3926,15 +3745,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/use-aria-button/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-aria-link": {
"version": "2.2.5",
"resolved": "https://registry.npmjs.org/@nextui-org/use-aria-link/-/use-aria-link-2.2.5.tgz",
@@ -3977,15 +3787,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/use-aria-link/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-aria-modal-overlay": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/@nextui-org/use-aria-modal-overlay/-/use-aria-modal-overlay-2.2.3.tgz",
@@ -4050,15 +3851,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/use-aria-modal-overlay/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-aria-multiselect": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/@nextui-org/use-aria-multiselect/-/use-aria-multiselect-2.4.3.tgz",
@@ -4129,15 +3921,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/use-aria-multiselect/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-callback-ref": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@nextui-org/use-callback-ref/-/use-callback-ref-2.1.1.tgz",
@@ -4213,15 +3996,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/use-disclosure/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-draggable": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@nextui-org/use-draggable/-/use-draggable-2.1.2.tgz",
@@ -4287,15 +4061,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/use-intersection-observer/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nextui-org/use-is-mobile": {
"version": "2.2.2",
"resolved": "https://registry.npmjs.org/@nextui-org/use-is-mobile/-/use-is-mobile-2.2.2.tgz",
@@ -4421,19 +4186,11 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@nextui-org/user/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
"integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.stat": "2.0.5",
@@ -4447,6 +4204,7 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
"integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -4456,6 +4214,7 @@
"version": "1.2.8",
"resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
"integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"@nodelib/fs.scandir": "2.1.5",
@@ -4479,6 +4238,7 @@
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
"integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
"license": "MIT",
"optional": true,
"engines": {
@@ -4566,9 +4326,9 @@
}
},
"node_modules/@react-aria/checkbox/node_modules/@react-aria/ssr": {
- "version": "3.9.8",
- "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
- "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -4581,16 +4341,16 @@
}
},
"node_modules/@react-aria/checkbox/node_modules/@react-aria/toggle": {
- "version": "3.11.2",
- "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.11.2.tgz",
- "integrity": "sha512-JOg8yYYCjLDnEpuggPo9GyXFaT/B238d3R8i/xQ6KLelpi3fXdJuZlFD6n9NQp3DJbE8Wj+wM5/VFFAi3cISpw==",
+ "version": "3.12.3",
+ "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.12.3.tgz",
+ "integrity": "sha512-mciUbeVP99fRObnH5qLFrkKXX+5VKeV6BhFJlmz1eo3ltR/0xZKnUcycA2CGzmqtB70w09CAhr8NMEnpNH8dwQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/toggle": "^3.8.3",
- "@react-types/checkbox": "^3.9.3",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/toggle": "^3.9.3",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4599,15 +4359,15 @@
}
},
"node_modules/@react-aria/checkbox/node_modules/@react-aria/toggle/node_modules/@react-aria/interactions": {
- "version": "3.25.0",
- "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz",
- "integrity": "sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==",
+ "version": "3.26.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.26.0.tgz",
+ "integrity": "sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/ssr": "^3.9.8",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/flags": "^3.1.1",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4616,14 +4376,14 @@
}
},
"node_modules/@react-aria/checkbox/node_modules/@react-stately/toggle": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.3.tgz",
- "integrity": "sha512-4T2V3P1RK4zEFz4vJjUXUXyB0g4Slm6stE6Ry20fzDWjltuW42cD2lmrd7ccTO/CXFmHLECcXQLD4GEbOj0epA==",
+ "version": "3.9.3",
+ "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.9.3.tgz",
+ "integrity": "sha512-G6aA/aTnid/6dQ9dxNEd7/JqzRmVkVYYpOAP+l02hepiuSmFwLu4nE98i4YFBQqFZ5b4l01gMrS90JGL7HrNmw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/utils": "^3.10.6",
- "@react-types/checkbox": "^3.9.3",
- "@react-types/shared": "^3.29.0",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4631,12 +4391,12 @@
}
},
"node_modules/@react-aria/checkbox/node_modules/@react-types/checkbox": {
- "version": "3.9.3",
- "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.3.tgz",
- "integrity": "sha512-h6wmK7CraKHKE6L13Ut+CtnjRktbMRhkCSorv7eg82M6p4PDhZ7mfDSh13IlGR4sryT8Ka+aOjOU+EvMrKiduA==",
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.10.2.tgz",
+ "integrity": "sha512-ktPkl6ZfIdGS1tIaGSU/2S5Agf2NvXI9qAgtdMDNva0oLyAZ4RLQb6WecPvofw1J7YKXu0VA5Mu7nlX+FM2weQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
@@ -4733,15 +4493,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@react-aria/focus/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@react-aria/form": {
"version": "3.0.11",
"resolved": "https://registry.npmjs.org/@react-aria/form/-/form-3.0.11.tgz",
@@ -4759,23 +4510,23 @@
}
},
"node_modules/@react-aria/grid": {
- "version": "3.13.0",
- "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.13.0.tgz",
- "integrity": "sha512-RcuJYA4fyJ83MH3SunU+P5BGkx3LJdQ6kxwqwWGIuI9eUKc7uVbqvN9WN3fI+L0QfxqBFmh7ffRxIdQn7puuzw==",
- "license": "Apache-2.0",
- "dependencies": {
- "@react-aria/focus": "^3.20.2",
- "@react-aria/i18n": "^3.12.8",
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/live-announcer": "^3.4.2",
- "@react-aria/selection": "^3.24.0",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/collections": "^3.12.3",
- "@react-stately/grid": "^3.11.1",
- "@react-stately/selection": "^3.20.1",
- "@react-types/checkbox": "^3.9.3",
- "@react-types/grid": "^3.3.1",
- "@react-types/shared": "^3.29.0",
+ "version": "3.14.6",
+ "resolved": "https://registry.npmjs.org/@react-aria/grid/-/grid-3.14.6.tgz",
+ "integrity": "sha512-xagBKHNPu4Ovt/I5He7T/oIEq82MDMSrRi5Sw3oxSCwwtZpv+7eyKRSrFz9vrNUzNgWCcx5VHLE660bLdeVNDQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.21.3",
+ "@react-aria/i18n": "^3.12.14",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/live-announcer": "^3.4.4",
+ "@react-aria/selection": "^3.27.0",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/grid": "^3.11.7",
+ "@react-stately/selection": "^3.20.7",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/grid": "^3.3.6",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4784,14 +4535,14 @@
}
},
"node_modules/@react-aria/grid/node_modules/@react-aria/focus": {
- "version": "3.20.2",
- "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.2.tgz",
- "integrity": "sha512-Q3rouk/rzoF/3TuH6FzoAIKrl+kzZi9LHmr8S5EqLAOyP9TXIKG34x2j42dZsAhrw7TbF9gA8tBKwnCNH4ZV+Q==",
+ "version": "3.21.3",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.3.tgz",
+ "integrity": "sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/utils": "^3.28.2",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/utils": "^3.32.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0",
"clsx": "^2.0.0"
},
@@ -4801,15 +4552,15 @@
}
},
"node_modules/@react-aria/grid/node_modules/@react-aria/interactions": {
- "version": "3.25.0",
- "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz",
- "integrity": "sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==",
+ "version": "3.26.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.26.0.tgz",
+ "integrity": "sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/ssr": "^3.9.8",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/flags": "^3.1.1",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4818,17 +4569,17 @@
}
},
"node_modules/@react-aria/grid/node_modules/@react-aria/selection": {
- "version": "3.24.0",
- "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.24.0.tgz",
- "integrity": "sha512-RfGXVc04zz41NVIW89/a3quURZ4LT/GJLkiajQK2VjhisidPdrAWkcfjjWJj0n+tm5gPWbi9Rs5R/Rc8mrvq8Q==",
+ "version": "3.27.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/selection/-/selection-3.27.0.tgz",
+ "integrity": "sha512-4zgreuCu4QM4t2U7aF3mbMvIKCEkTEo6h6nGJvbyZALZ/eFtLTvUiV8/5CGDJRLGvgMvi3XxUeF9PZbpk5nMJg==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/focus": "^3.20.2",
- "@react-aria/i18n": "^3.12.8",
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/selection": "^3.20.1",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/focus": "^3.21.3",
+ "@react-aria/i18n": "^3.12.14",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/selection": "^3.20.7",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4837,9 +4588,9 @@
}
},
"node_modules/@react-aria/grid/node_modules/@react-aria/ssr": {
- "version": "3.9.8",
- "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
- "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -4852,12 +4603,12 @@
}
},
"node_modules/@react-aria/grid/node_modules/@react-stately/collections": {
- "version": "3.12.3",
- "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.3.tgz",
- "integrity": "sha512-QfSBME2QWDjUw/RmmUjrYl/j1iCYcYCIDsgZda1OeRtt63R11k0aqmmwrDRwCsA+Sv+D5QgkOp4KK+CokTzoVQ==",
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4865,51 +4616,42 @@
}
},
"node_modules/@react-aria/grid/node_modules/@react-types/checkbox": {
- "version": "3.9.3",
- "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.3.tgz",
- "integrity": "sha512-h6wmK7CraKHKE6L13Ut+CtnjRktbMRhkCSorv7eg82M6p4PDhZ7mfDSh13IlGR4sryT8Ka+aOjOU+EvMrKiduA==",
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.10.2.tgz",
+ "integrity": "sha512-ktPkl6ZfIdGS1tIaGSU/2S5Agf2NvXI9qAgtdMDNva0oLyAZ4RLQb6WecPvofw1J7YKXu0VA5Mu7nlX+FM2weQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-aria/grid/node_modules/@react-types/grid": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.1.tgz",
- "integrity": "sha512-bPDckheJiHSIzSeSkLqrO6rXRLWvciFJr9rpCjq/+wBj6HsLh2iMpkB/SqmRHTGpPlJvlu0b7AlxK1FYE0QSKA==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.6.tgz",
+ "integrity": "sha512-vIZJlYTii2n1We9nAugXwM2wpcpsC6JigJFBd6vGhStRdRWRoU4yv1Gc98Usbx0FQ/J7GLVIgeG8+1VMTKBdxw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@react-aria/grid/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@react-aria/i18n": {
- "version": "3.12.8",
- "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.8.tgz",
- "integrity": "sha512-V/Nau9WuwTwxfFffQL4URyKyY2HhRlu9zmzkF2Hw/j5KmEQemD+9jfaLueG2CJu85lYL06JrZXUdnhZgKnqMkA==",
- "license": "Apache-2.0",
- "dependencies": {
- "@internationalized/date": "^3.8.0",
- "@internationalized/message": "^3.1.7",
- "@internationalized/number": "^3.6.1",
- "@internationalized/string": "^3.2.6",
- "@react-aria/ssr": "^3.9.8",
- "@react-aria/utils": "^3.28.2",
- "@react-types/shared": "^3.29.0",
+ "version": "3.12.14",
+ "resolved": "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.14.tgz",
+ "integrity": "sha512-zYvs1FlLamFD49uneX3i5mPHrAsB3OjVpSWApTcPw8ydxOaphQDp/Q1aqrbcxlrQCcxZdXWHuvLlbkNR4+8jzw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@internationalized/date": "^3.10.1",
+ "@internationalized/message": "^3.1.8",
+ "@internationalized/number": "^3.6.5",
+ "@internationalized/string": "^3.2.7",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.32.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -4918,9 +4660,9 @@
}
},
"node_modules/@react-aria/i18n/node_modules/@react-aria/ssr": {
- "version": "3.9.8",
- "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
- "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -5000,9 +4742,9 @@
}
},
"node_modules/@react-aria/live-announcer": {
- "version": "3.4.2",
- "resolved": "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.4.2.tgz",
- "integrity": "sha512-6+yNF9ZrZ4YJ60Oxy2gKI4/xy6WUv1iePDCFJkgpNVuOEYi8W8czff8ctXu/RPB25OJx5v2sCw9VirRogTo2zA==",
+ "version": "3.4.4",
+ "resolved": "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.4.4.tgz",
+ "integrity": "sha512-PTTBIjNRnrdJOIRTDGNifY2d//kA7GUAwRFJNOEwSNG4FW+Bq9awqLiflw0JkpyB0VNIwou6lqKPHZVLsGWOXA==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -5035,21 +4777,21 @@
}
},
"node_modules/@react-aria/overlays": {
- "version": "3.27.0",
- "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.27.0.tgz",
- "integrity": "sha512-2vZVgL7FrloN5Rh8sAhadGADJbuWg69DdSJB3fd2/h5VvcEhnIfNPu9Ma5XmdkApDoTboIEsKZ4QLYwRl98w6w==",
- "license": "Apache-2.0",
- "dependencies": {
- "@react-aria/focus": "^3.20.2",
- "@react-aria/i18n": "^3.12.8",
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/ssr": "^3.9.8",
- "@react-aria/utils": "^3.28.2",
- "@react-aria/visually-hidden": "^3.8.22",
- "@react-stately/overlays": "^3.6.15",
- "@react-types/button": "^3.12.0",
- "@react-types/overlays": "^3.8.14",
- "@react-types/shared": "^3.29.0",
+ "version": "3.31.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.31.0.tgz",
+ "integrity": "sha512-Vq41X1s8XheGIhGbbuqRJslJEX08qmMVX//dwuBaFX9T18mMR04tumKOMxp8Lz+vqwdGLvjNUYDMcgolL+AMjw==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@react-aria/focus": "^3.21.3",
+ "@react-aria/i18n": "^3.12.14",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.32.0",
+ "@react-aria/visually-hidden": "^3.8.29",
+ "@react-stately/overlays": "^3.6.21",
+ "@react-types/button": "^3.14.1",
+ "@react-types/overlays": "^3.9.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5058,14 +4800,14 @@
}
},
"node_modules/@react-aria/overlays/node_modules/@react-aria/focus": {
- "version": "3.20.2",
- "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.2.tgz",
- "integrity": "sha512-Q3rouk/rzoF/3TuH6FzoAIKrl+kzZi9LHmr8S5EqLAOyP9TXIKG34x2j42dZsAhrw7TbF9gA8tBKwnCNH4ZV+Q==",
+ "version": "3.21.3",
+ "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.3.tgz",
+ "integrity": "sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/utils": "^3.28.2",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/utils": "^3.32.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0",
"clsx": "^2.0.0"
},
@@ -5075,15 +4817,15 @@
}
},
"node_modules/@react-aria/overlays/node_modules/@react-aria/interactions": {
- "version": "3.25.0",
- "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz",
- "integrity": "sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==",
+ "version": "3.26.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.26.0.tgz",
+ "integrity": "sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/ssr": "^3.9.8",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/flags": "^3.1.1",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5092,9 +4834,9 @@
}
},
"node_modules/@react-aria/overlays/node_modules/@react-aria/ssr": {
- "version": "3.9.8",
- "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
- "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -5107,14 +4849,14 @@
}
},
"node_modules/@react-aria/overlays/node_modules/@react-aria/visually-hidden": {
- "version": "3.8.22",
- "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.22.tgz",
- "integrity": "sha512-EO3R8YTKZ7HkLl9k1Y2uBKYBgpJagth4/4W7mfpJZE24A3fQnCP8zx1sweXiAm0mirR4J6tNaK7Ia8ssP5TpOw==",
+ "version": "3.8.29",
+ "resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.29.tgz",
+ "integrity": "sha512-1joCP+MHBLd+YA6Gb08nMFfDBhOF0Kh1gR1SA8zoxEB5RMfQEEkufIB8k0GGwvHGSCK3gFyO8UAVsD0+rRYEyg==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/utils": "^3.28.2",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/utils": "^3.32.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5123,13 +4865,13 @@
}
},
"node_modules/@react-aria/overlays/node_modules/@react-stately/overlays": {
- "version": "3.6.15",
- "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.15.tgz",
- "integrity": "sha512-LBaGpXuI+SSd5HSGzyGJA0Gy09V2tl2G/r0lllTYqwt0RDZR6p7IrhdGVXZm6vI0oWEnih7yLC32krkVQrffgQ==",
+ "version": "3.6.21",
+ "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.21.tgz",
+ "integrity": "sha512-7f25H1PS2g+SNvuWPEW30pSGqYNHxesCP4w+1RcV/XV1oQI7oP5Ji2WfI0QsJEFc9wP/ZO1pyjHNKpfLI3O88g==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/utils": "^3.10.6",
- "@react-types/overlays": "^3.8.14",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/overlays": "^3.9.2",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5137,38 +4879,29 @@
}
},
"node_modules/@react-aria/overlays/node_modules/@react-types/button": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.12.0.tgz",
- "integrity": "sha512-YrASNa+RqGQpzJcxNAahzNuTYVID1OE6HCorrEOXIyGS3EGogHsQmFs9OyThXnGHq6q4rLlA806/jWbP9uZdxA==",
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.14.1.tgz",
+ "integrity": "sha512-D8C4IEwKB7zEtiWYVJ3WE/5HDcWlze9mLWQ5hfsBfpePyWCgO3bT/+wjb/7pJvcAocrkXo90QrMm85LcpBtrpg==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-aria/overlays/node_modules/@react-types/overlays": {
- "version": "3.8.14",
- "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.14.tgz",
- "integrity": "sha512-XJS67KHYhdMvPNHXNGdmc85gE+29QT5TwC58V4kxxHVtQh9fYzEEPzIV8K84XWSz04rRGe3fjDgRNbcqBektWQ==",
+ "version": "3.9.2",
+ "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.9.2.tgz",
+ "integrity": "sha512-Q0cRPcBGzNGmC8dBuHyoPR7N3057KTS5g+vZfQ53k8WwmilXBtemFJPLsogJbspuewQ/QJ3o2HYsp2pne7/iNw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@react-aria/overlays/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@react-aria/progress": {
"version": "3.4.18",
"resolved": "https://registry.npmjs.org/@react-aria/progress/-/progress-3.4.18.tgz",
@@ -5247,16 +4980,16 @@
}
},
"node_modules/@react-aria/spinbutton": {
- "version": "3.6.14",
- "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.6.14.tgz",
- "integrity": "sha512-oSKe9p0Q/7W39eXRnLxlwJG5dQo4ffosRT3u2AtOcFkk2Zzj+tSQFzHQ4202nrWdzRnQ2KLTgUUNnUvXf0BJcg==",
+ "version": "3.7.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.7.0.tgz",
+ "integrity": "sha512-FOyH94BZp+jNhUJuZqXSubQZDNQEJyW/J19/gwCxQvQvxAP79dhDFshh1UtrL4EjbjIflmaOes+sH/XEHUnJVA==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/i18n": "^3.12.8",
- "@react-aria/live-announcer": "^3.4.2",
- "@react-aria/utils": "^3.28.2",
- "@react-types/button": "^3.12.0",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/i18n": "^3.12.14",
+ "@react-aria/live-announcer": "^3.4.4",
+ "@react-aria/utils": "^3.32.0",
+ "@react-types/button": "^3.14.1",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5265,12 +4998,12 @@
}
},
"node_modules/@react-aria/spinbutton/node_modules/@react-types/button": {
- "version": "3.12.0",
- "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.12.0.tgz",
- "integrity": "sha512-YrASNa+RqGQpzJcxNAahzNuTYVID1OE6HCorrEOXIyGS3EGogHsQmFs9OyThXnGHq6q4rLlA806/jWbP9uZdxA==",
+ "version": "3.14.1",
+ "resolved": "https://registry.npmjs.org/@react-types/button/-/button-3.14.1.tgz",
+ "integrity": "sha512-D8C4IEwKB7zEtiWYVJ3WE/5HDcWlze9mLWQ5hfsBfpePyWCgO3bT/+wjb/7pJvcAocrkXo90QrMm85LcpBtrpg==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
@@ -5308,9 +5041,9 @@
}
},
"node_modules/@react-aria/switch/node_modules/@react-aria/ssr": {
- "version": "3.9.8",
- "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
- "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -5323,16 +5056,16 @@
}
},
"node_modules/@react-aria/switch/node_modules/@react-aria/toggle": {
- "version": "3.11.2",
- "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.11.2.tgz",
- "integrity": "sha512-JOg8yYYCjLDnEpuggPo9GyXFaT/B238d3R8i/xQ6KLelpi3fXdJuZlFD6n9NQp3DJbE8Wj+wM5/VFFAi3cISpw==",
+ "version": "3.12.3",
+ "resolved": "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.12.3.tgz",
+ "integrity": "sha512-mciUbeVP99fRObnH5qLFrkKXX+5VKeV6BhFJlmz1eo3ltR/0xZKnUcycA2CGzmqtB70w09CAhr8NMEnpNH8dwQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/interactions": "^3.25.0",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/toggle": "^3.8.3",
- "@react-types/checkbox": "^3.9.3",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/interactions": "^3.26.0",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/toggle": "^3.9.3",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5341,15 +5074,15 @@
}
},
"node_modules/@react-aria/switch/node_modules/@react-aria/toggle/node_modules/@react-aria/interactions": {
- "version": "3.25.0",
- "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz",
- "integrity": "sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==",
+ "version": "3.26.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.26.0.tgz",
+ "integrity": "sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/ssr": "^3.9.8",
- "@react-aria/utils": "^3.28.2",
- "@react-stately/flags": "^3.1.1",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-aria/utils": "^3.32.0",
+ "@react-stately/flags": "^3.1.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5358,14 +5091,14 @@
}
},
"node_modules/@react-aria/switch/node_modules/@react-stately/toggle": {
- "version": "3.8.3",
- "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.3.tgz",
- "integrity": "sha512-4T2V3P1RK4zEFz4vJjUXUXyB0g4Slm6stE6Ry20fzDWjltuW42cD2lmrd7ccTO/CXFmHLECcXQLD4GEbOj0epA==",
+ "version": "3.9.3",
+ "resolved": "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.9.3.tgz",
+ "integrity": "sha512-G6aA/aTnid/6dQ9dxNEd7/JqzRmVkVYYpOAP+l02hepiuSmFwLu4nE98i4YFBQqFZ5b4l01gMrS90JGL7HrNmw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/utils": "^3.10.6",
- "@react-types/checkbox": "^3.9.3",
- "@react-types/shared": "^3.29.0",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/checkbox": "^3.10.2",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5373,12 +5106,12 @@
}
},
"node_modules/@react-aria/switch/node_modules/@react-types/checkbox": {
- "version": "3.9.3",
- "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.3.tgz",
- "integrity": "sha512-h6wmK7CraKHKE6L13Ut+CtnjRktbMRhkCSorv7eg82M6p4PDhZ7mfDSh13IlGR4sryT8Ka+aOjOU+EvMrKiduA==",
+ "version": "3.10.2",
+ "resolved": "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.10.2.tgz",
+ "integrity": "sha512-ktPkl6ZfIdGS1tIaGSU/2S5Agf2NvXI9qAgtdMDNva0oLyAZ4RLQb6WecPvofw1J7YKXu0VA5Mu7nlX+FM2weQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
@@ -5486,15 +5219,15 @@
}
},
"node_modules/@react-aria/utils": {
- "version": "3.28.2",
- "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.28.2.tgz",
- "integrity": "sha512-J8CcLbvnQgiBn54eeEvQQbIOfBF3A1QizxMw9P4cl9MkeR03ug7RnjTIdJY/n2p7t59kLeAB3tqiczhcj+Oi5w==",
+ "version": "3.32.0",
+ "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.32.0.tgz",
+ "integrity": "sha512-/7Rud06+HVBIlTwmwmJa2W8xVtgxgzm0+kLbuFooZRzKDON6hhozS1dOMR/YLMxyJOaYOTpImcP4vRR9gL1hEg==",
"license": "Apache-2.0",
"dependencies": {
- "@react-aria/ssr": "^3.9.8",
- "@react-stately/flags": "^3.1.1",
- "@react-stately/utils": "^3.10.6",
- "@react-types/shared": "^3.29.0",
+ "@react-aria/ssr": "^3.9.10",
+ "@react-stately/flags": "^3.1.2",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0",
"clsx": "^2.0.0"
},
@@ -5504,9 +5237,9 @@
}
},
"node_modules/@react-aria/utils/node_modules/@react-aria/ssr": {
- "version": "3.9.8",
- "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz",
- "integrity": "sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==",
+ "version": "3.9.10",
+ "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz",
+ "integrity": "sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -5518,15 +5251,6 @@
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
- "node_modules/@react-aria/utils/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/@react-aria/visually-hidden": {
"version": "3.8.18",
"resolved": "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.18.tgz",
@@ -5543,9 +5267,9 @@
}
},
"node_modules/@react-google-maps/api": {
- "version": "2.20.7",
- "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.20.7.tgz",
- "integrity": "sha512-ys7uri3V6gjhYZUI43srHzSKDC6/jiKTwHNlwXFTvjeaJE3M3OaYBt9FZKvJs8qnOhL6i6nD1BKJoi1KrnkCkg==",
+ "version": "2.20.8",
+ "resolved": "https://registry.npmjs.org/@react-google-maps/api/-/api-2.20.8.tgz",
+ "integrity": "sha512-wtLYFtCGXK3qbIz1H5to3JxbosPnKsvjDKhqGylXUb859EskhzR7OpuNt0LqdLarXUtZCJTKzPn3BNaekNIahg==",
"license": "MIT",
"dependencies": {
"@googlemaps/js-api-loader": "1.16.8",
@@ -5657,9 +5381,9 @@
}
},
"node_modules/@react-stately/flags": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.1.tgz",
- "integrity": "sha512-XPR5gi5LfrPdhxZzdIlJDz/B5cBf63l4q6/AzNqVWFKgd0QqY5LvWJftXkklaIUpKSJkIKQb8dphuZXDtkWNqg==",
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz",
+ "integrity": "sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -5679,15 +5403,15 @@
}
},
"node_modules/@react-stately/grid": {
- "version": "3.11.1",
- "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.11.1.tgz",
- "integrity": "sha512-xMk2YsaIKkF8dInRLUFpUXBIqnYt88hehhq2nb65RFgsFFhngE/OkaFudSUzaYPc1KvHpW+oHqvseC+G1iDG2w==",
+ "version": "3.11.7",
+ "resolved": "https://registry.npmjs.org/@react-stately/grid/-/grid-3.11.7.tgz",
+ "integrity": "sha512-SqzBSxUTFZKLZicfXDK+M0A3gh07AYK1pmU/otcq2cjZ0nSC4CceKijQ2GBZnl+YGcGHI1RgkhpLP6ZioMYctQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/collections": "^3.12.3",
- "@react-stately/selection": "^3.20.1",
- "@react-types/grid": "^3.3.1",
- "@react-types/shared": "^3.29.0",
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/selection": "^3.20.7",
+ "@react-types/grid": "^3.3.6",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5695,12 +5419,12 @@
}
},
"node_modules/@react-stately/grid/node_modules/@react-stately/collections": {
- "version": "3.12.3",
- "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.3.tgz",
- "integrity": "sha512-QfSBME2QWDjUw/RmmUjrYl/j1iCYcYCIDsgZda1OeRtt63R11k0aqmmwrDRwCsA+Sv+D5QgkOp4KK+CokTzoVQ==",
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5708,12 +5432,12 @@
}
},
"node_modules/@react-stately/grid/node_modules/@react-types/grid": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.1.tgz",
- "integrity": "sha512-bPDckheJiHSIzSeSkLqrO6rXRLWvciFJr9rpCjq/+wBj6HsLh2iMpkB/SqmRHTGpPlJvlu0b7AlxK1FYE0QSKA==",
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.6.tgz",
+ "integrity": "sha512-vIZJlYTii2n1We9nAugXwM2wpcpsC6JigJFBd6vGhStRdRWRoU4yv1Gc98Usbx0FQ/J7GLVIgeG8+1VMTKBdxw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
@@ -5781,16 +5505,17 @@
}
},
"node_modules/@react-stately/select": {
- "version": "3.6.12",
- "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.6.12.tgz",
- "integrity": "sha512-5o/NAaENO/Gxs1yui5BHLItxLnDPSQJ5HDKycuD0/gGC17BboAGEY/F9masiQ5qwRPe3JEc0QfvMRq3yZVNXog==",
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/@react-stately/select/-/select-3.9.0.tgz",
+ "integrity": "sha512-eNE33zVYpVdCPKRPGYyViN3LnEq82e1wjBIrs9T7Vo4EBnJeT57pqMZpalTPk7qsA+861t14Qrj7GnUd+YbEXw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/form": "^3.1.3",
- "@react-stately/list": "^3.12.1",
- "@react-stately/overlays": "^3.6.15",
- "@react-types/select": "^3.9.11",
- "@react-types/shared": "^3.29.0",
+ "@react-stately/form": "^3.2.2",
+ "@react-stately/list": "^3.13.2",
+ "@react-stately/overlays": "^3.6.21",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/select": "^3.12.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5798,12 +5523,12 @@
}
},
"node_modules/@react-stately/select/node_modules/@react-stately/collections": {
- "version": "3.12.3",
- "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.3.tgz",
- "integrity": "sha512-QfSBME2QWDjUw/RmmUjrYl/j1iCYcYCIDsgZda1OeRtt63R11k0aqmmwrDRwCsA+Sv+D5QgkOp4KK+CokTzoVQ==",
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5811,12 +5536,12 @@
}
},
"node_modules/@react-stately/select/node_modules/@react-stately/form": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.1.3.tgz",
- "integrity": "sha512-Jisgm0facSS3sAzHfSgshoCo3LxfO0wmQj98MOBCGXyVL+MSwx2ilb38eXIyBCzHJzJnPRTLaK/E4T49aph47A==",
+ "version": "3.2.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/form/-/form-3.2.2.tgz",
+ "integrity": "sha512-soAheOd7oaTO6eNs6LXnfn0tTqvOoe3zN9FvtIhhrErKz9XPc5sUmh3QWwR45+zKbitOi1HOjfA/gifKhZcfWw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5824,15 +5549,15 @@
}
},
"node_modules/@react-stately/select/node_modules/@react-stately/list": {
- "version": "3.12.1",
- "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.12.1.tgz",
- "integrity": "sha512-N+YCInNZ2OpY0WUNvJWUTyFHtzE5yBtZ9DI4EHJDvm61+jmZ2s3HszOfa7j+7VOKq78VW3m5laqsQNWvMrLFrQ==",
+ "version": "3.13.2",
+ "resolved": "https://registry.npmjs.org/@react-stately/list/-/list-3.13.2.tgz",
+ "integrity": "sha512-dGFALuQWNNOkv7W12qSsXLF4mJHLeWeK2hVvdyj4SI8Vxku+BOfaVKuW3sn3mNiixI1dM/7FY2ip4kK+kv27vw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/collections": "^3.12.3",
- "@react-stately/selection": "^3.20.1",
- "@react-stately/utils": "^3.10.6",
- "@react-types/shared": "^3.29.0",
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/selection": "^3.20.7",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5840,13 +5565,13 @@
}
},
"node_modules/@react-stately/select/node_modules/@react-stately/overlays": {
- "version": "3.6.15",
- "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.15.tgz",
- "integrity": "sha512-LBaGpXuI+SSd5HSGzyGJA0Gy09V2tl2G/r0lllTYqwt0RDZR6p7IrhdGVXZm6vI0oWEnih7yLC32krkVQrffgQ==",
+ "version": "3.6.21",
+ "resolved": "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.21.tgz",
+ "integrity": "sha512-7f25H1PS2g+SNvuWPEW30pSGqYNHxesCP4w+1RcV/XV1oQI7oP5Ji2WfI0QsJEFc9wP/ZO1pyjHNKpfLI3O88g==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/utils": "^3.10.6",
- "@react-types/overlays": "^3.8.14",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/overlays": "^3.9.2",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5854,38 +5579,38 @@
}
},
"node_modules/@react-stately/select/node_modules/@react-types/overlays": {
- "version": "3.8.14",
- "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.8.14.tgz",
- "integrity": "sha512-XJS67KHYhdMvPNHXNGdmc85gE+29QT5TwC58V4kxxHVtQh9fYzEEPzIV8K84XWSz04rRGe3fjDgRNbcqBektWQ==",
+ "version": "3.9.2",
+ "resolved": "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.9.2.tgz",
+ "integrity": "sha512-Q0cRPcBGzNGmC8dBuHyoPR7N3057KTS5g+vZfQ53k8WwmilXBtemFJPLsogJbspuewQ/QJ3o2HYsp2pne7/iNw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-stately/select/node_modules/@react-types/select": {
- "version": "3.9.11",
- "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.9.11.tgz",
- "integrity": "sha512-uEpQCgDlrq/5fW05FgNEsqsqpvZVKfHQO9Mp7OTqGtm4UBNAbcQ6hOV7MJwQCS25Lu2luzOYdgqDUN8eAATJVQ==",
+ "version": "3.12.0",
+ "resolved": "https://registry.npmjs.org/@react-types/select/-/select-3.12.0.tgz",
+ "integrity": "sha512-tM3mEbQNotvCJs1gYRFyIeXmXrIBSBLGw7feCIaYSO45IyjCGv8NZwpQWjoKPaWo3GpbHfHMNlWlq3v5QQPIXw==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-stately/selection": {
- "version": "3.20.1",
- "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.20.1.tgz",
- "integrity": "sha512-K9MP6Rfg2yvFoY2Cr+ykA7bP4EBXlGaq5Dqfa1krvcXlEgMbQka5muLHdNXqjzGgcwPmS1dx1NECD15q63NtOw==",
+ "version": "3.20.7",
+ "resolved": "https://registry.npmjs.org/@react-stately/selection/-/selection-3.20.7.tgz",
+ "integrity": "sha512-NkiRsNCfORBIHNF1bCavh4Vvj+Yd5NffE10iXtaFuhF249NlxLynJZmkcVCqNP9taC2pBIHX00+9tcBgxhG+mA==",
"license": "Apache-2.0",
"dependencies": {
- "@react-stately/collections": "^3.12.3",
- "@react-stately/utils": "^3.10.6",
- "@react-types/shared": "^3.29.0",
+ "@react-stately/collections": "^3.12.8",
+ "@react-stately/utils": "^3.11.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -5893,12 +5618,12 @@
}
},
"node_modules/@react-stately/selection/node_modules/@react-stately/collections": {
- "version": "3.12.3",
- "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.3.tgz",
- "integrity": "sha512-QfSBME2QWDjUw/RmmUjrYl/j1iCYcYCIDsgZda1OeRtt63R11k0aqmmwrDRwCsA+Sv+D5QgkOp4KK+CokTzoVQ==",
+ "version": "3.12.8",
+ "resolved": "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz",
+ "integrity": "sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0",
+ "@react-types/shared": "^3.32.1",
"@swc/helpers": "^0.5.0"
},
"peerDependencies": {
@@ -6001,9 +5726,9 @@
}
},
"node_modules/@react-stately/utils": {
- "version": "3.10.6",
- "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.6.tgz",
- "integrity": "sha512-O76ip4InfTTzAJrg8OaZxKU4vvjMDOpfA/PGNOytiXwBbkct2ZeZwaimJ8Bt9W1bj5VsZ81/o/tW4BacbdDOMA==",
+ "version": "3.11.0",
+ "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.11.0.tgz",
+ "integrity": "sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==",
"license": "Apache-2.0",
"dependencies": {
"@swc/helpers": "^0.5.0"
@@ -6202,12 +5927,12 @@
}
},
"node_modules/@react-types/listbox": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.6.0.tgz",
- "integrity": "sha512-+1ugDKTxson/WNOQZO4BfrnQ6cGDt+72mEytXMsSsd4aEC+x3RyUv6NKwdOl4n602cOreo0MHtap1X2BOACVoQ==",
+ "version": "3.7.4",
+ "resolved": "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.7.4.tgz",
+ "integrity": "sha512-p4YEpTl/VQGrqVE8GIfqTS5LkT5jtjDTbVeZgrkPnX/fiPhsfbTPiZ6g0FNap4+aOGJFGEEZUv2q4vx+rCORww==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
@@ -6275,33 +6000,33 @@
}
},
"node_modules/@react-types/shared": {
- "version": "3.29.0",
- "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.29.0.tgz",
- "integrity": "sha512-IDQYu/AHgZimObzCFdNl1LpZvQW/xcfLt3v20sorl5qRucDVj4S9os98sVTZ4IRIBjmS+MkjqpR5E70xan7ooA==",
+ "version": "3.32.1",
+ "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz",
+ "integrity": "sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==",
"license": "Apache-2.0",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-types/slider": {
- "version": "3.7.10",
- "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.7.10.tgz",
- "integrity": "sha512-Yb8wbpu2gS7AwvJUuz0IdZBRi6eIBZq32BSss4UHX0StA8dtR1/K4JeTsArxwiA3P0BA6t0gbR6wzxCvVA9fRw==",
+ "version": "3.8.2",
+ "resolved": "https://registry.npmjs.org/@react-types/slider/-/slider-3.8.2.tgz",
+ "integrity": "sha512-MQYZP76OEOYe7/yA2To+Dl0LNb0cKKnvh5JtvNvDnAvEprn1RuLiay8Oi/rTtXmc2KmBa4VdTcsXsmkbbkeN2Q==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
}
},
"node_modules/@react-types/switch": {
- "version": "3.5.10",
- "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.10.tgz",
- "integrity": "sha512-YyNhx4CvuJ0Rvv7yMuQaqQuOIeg+NwLV00NHHJ+K0xEANSLcICLOLPNMOqRIqLSQDz5vDI705UKk8gVcxqPX5g==",
+ "version": "3.5.15",
+ "resolved": "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.15.tgz",
+ "integrity": "sha512-r/ouGWQmIeHyYSP1e5luET+oiR7N7cLrAlWsrAfYRWHxqXOSNQloQnZJ3PLHrKFT02fsrQhx2rHaK2LfKeyN3A==",
"license": "Apache-2.0",
"dependencies": {
- "@react-types/shared": "^3.29.0"
+ "@react-types/shared": "^3.32.1"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1"
@@ -6437,12 +6162,6 @@
"deprecated": "The '@studio-freight/lenis' package has been renamed to 'lenis'. Please update your dependencies: npm install lenis and visit the documentation: https://www.npmjs.com/package/lenis",
"license": "MIT"
},
- "node_modules/@swc/counter": {
- "version": "0.1.3",
- "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
- "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
- "license": "Apache-2.0"
- },
"node_modules/@swc/helpers": {
"version": "0.5.17",
"resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz",
@@ -6452,118 +6171,389 @@
"tslib": "^2.8.0"
}
},
- "node_modules/@tanstack/react-virtual": {
- "version": "3.11.2",
- "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz",
- "integrity": "sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ==",
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz",
+ "integrity": "sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
- "@tanstack/virtual-core": "3.11.2"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
- },
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
- "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ "@jridgewell/remapping": "^2.3.4",
+ "enhanced-resolve": "^5.18.3",
+ "jiti": "^2.6.1",
+ "lightningcss": "1.30.2",
+ "magic-string": "^0.30.21",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.18"
}
},
- "node_modules/@tanstack/virtual-core": {
- "version": "3.11.2",
- "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz",
- "integrity": "sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==",
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz",
+ "integrity": "sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==",
+ "dev": true,
"license": "MIT",
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/tannerlinsley"
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.18",
+ "@tailwindcss/oxide-darwin-x64": "4.1.18",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.18",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.18",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.18",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.18",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.18",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.18"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz",
+ "integrity": "sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/@tybys/wasm-util": {
- "version": "0.9.0",
- "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
- "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz",
+ "integrity": "sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
"license": "MIT",
"optional": true,
- "dependencies": {
- "tslib": "^2.4.0"
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/@types/d3-array": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
- "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
- "license": "MIT"
- },
- "node_modules/@types/d3-color": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
- "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
- "license": "MIT"
- },
- "node_modules/@types/d3-ease": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
- "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
- "license": "MIT"
- },
- "node_modules/@types/d3-interpolate": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
- "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz",
+ "integrity": "sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "@types/d3-color": "*"
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/@types/d3-path": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
- "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
- "license": "MIT"
- },
- "node_modules/@types/d3-scale": {
- "version": "4.0.9",
- "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
- "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz",
+ "integrity": "sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "@types/d3-time": "*"
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/@types/d3-shape": {
- "version": "3.1.7",
- "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
- "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz",
+ "integrity": "sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "@types/d3-path": "*"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/@types/d3-time": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
- "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
- "license": "MIT"
- },
- "node_modules/@types/d3-timer": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
- "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
- "license": "MIT"
- },
- "node_modules/@types/debug": {
- "version": "4.1.12",
- "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
- "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz",
+ "integrity": "sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "@types/ms": "*"
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
}
},
- "node_modules/@types/estree": {
- "version": "1.0.8",
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz",
+ "integrity": "sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz",
+ "integrity": "sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz",
+ "integrity": "sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz",
+ "integrity": "sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1",
+ "@emnapi/wasi-threads": "^1.1.0",
+ "@napi-rs/wasm-runtime": "^1.1.0",
+ "@tybys/wasm-util": "^0.10.1",
+ "tslib": "^2.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz",
+ "integrity": "sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz",
+ "integrity": "sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz",
+ "integrity": "sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.18",
+ "@tailwindcss/oxide": "4.1.18",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.18"
+ }
+ },
+ "node_modules/@tanstack/react-virtual": {
+ "version": "3.11.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz",
+ "integrity": "sha512-OuFzMXPF4+xZgx8UzJha0AieuMihhhaWG0tCqpp6tDzlFwOmNBPYMuLOtMJ1Tr4pXLHmgjcWhG6RlknY2oNTdQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@tanstack/virtual-core": "3.11.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ },
+ "peerDependencies": {
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
+ "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/@tanstack/virtual-core": {
+ "version": "3.11.2",
+ "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz",
+ "integrity": "sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/tannerlinsley"
+ }
+ },
+ "node_modules/@tybys/wasm-util": {
+ "version": "0.9.0",
+ "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz",
+ "integrity": "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@types/d3-array": {
+ "version": "3.2.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz",
+ "integrity": "sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-color": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
+ "integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-ease": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
+ "integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-interpolate": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
+ "integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-color": "*"
+ }
+ },
+ "node_modules/@types/d3-path": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
+ "integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-scale": {
+ "version": "4.0.9",
+ "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
+ "integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-time": "*"
+ }
+ },
+ "node_modules/@types/d3-shape": {
+ "version": "3.1.7",
+ "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.7.tgz",
+ "integrity": "sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/d3-path": "*"
+ }
+ },
+ "node_modules/@types/d3-time": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
+ "integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
+ "license": "MIT"
+ },
+ "node_modules/@types/d3-timer": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
+ "integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
+ "license": "MIT"
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
"integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
"license": "MIT"
@@ -6600,9 +6590,9 @@
"license": "MIT"
},
"node_modules/@types/lodash": {
- "version": "4.17.16",
- "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz",
- "integrity": "sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==",
+ "version": "4.17.21",
+ "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz",
+ "integrity": "sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==",
"license": "MIT"
},
"node_modules/@types/lodash.debounce": {
@@ -6639,13 +6629,20 @@
"undici-types": "~6.21.0"
}
},
+ "node_modules/@types/prop-types": {
+ "version": "15.7.15",
+ "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz",
+ "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==",
+ "license": "MIT"
+ },
"node_modules/@types/react": {
- "version": "19.1.5",
- "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz",
- "integrity": "sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==",
+ "version": "18.3.27",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz",
+ "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==",
"license": "MIT",
"dependencies": {
- "csstype": "^3.0.2"
+ "@types/prop-types": "*",
+ "csstype": "^3.2.2"
}
},
"node_modules/@types/unist": {
@@ -6661,21 +6658,20 @@
"license": "MIT"
},
"node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz",
- "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==",
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz",
+ "integrity": "sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/regexpp": "^4.10.0",
- "@typescript-eslint/scope-manager": "8.32.1",
- "@typescript-eslint/type-utils": "8.32.1",
- "@typescript-eslint/utils": "8.32.1",
- "@typescript-eslint/visitor-keys": "8.32.1",
- "graphemer": "^1.4.0",
- "ignore": "^7.0.0",
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.52.0",
+ "@typescript-eslint/type-utils": "8.52.0",
+ "@typescript-eslint/utils": "8.52.0",
+ "@typescript-eslint/visitor-keys": "8.52.0",
+ "ignore": "^7.0.5",
"natural-compare": "^1.4.0",
- "ts-api-utils": "^2.1.0"
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6685,15 +6681,15 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0",
+ "@typescript-eslint/parser": "^8.52.0",
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
+ "typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz",
- "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==",
+ "version": "7.0.5",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz",
+ "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6701,17 +6697,17 @@
}
},
"node_modules/@typescript-eslint/parser": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz",
- "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==",
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz",
+ "integrity": "sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/scope-manager": "8.32.1",
- "@typescript-eslint/types": "8.32.1",
- "@typescript-eslint/typescript-estree": "8.32.1",
- "@typescript-eslint/visitor-keys": "8.32.1",
- "debug": "^4.3.4"
+ "@typescript-eslint/scope-manager": "8.52.0",
+ "@typescript-eslint/types": "8.52.0",
+ "@typescript-eslint/typescript-estree": "8.52.0",
+ "@typescript-eslint/visitor-keys": "8.52.0",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6722,18 +6718,19 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/scope-manager": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz",
- "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==",
+ "node_modules/@typescript-eslint/project-service": {
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz",
+ "integrity": "sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.32.1",
- "@typescript-eslint/visitor-keys": "8.32.1"
+ "@typescript-eslint/tsconfig-utils": "^8.52.0",
+ "@typescript-eslint/types": "^8.52.0",
+ "debug": "^4.4.3"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6741,19 +6738,20 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz",
- "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==",
+ "node_modules/@typescript-eslint/scope-manager": {
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz",
+ "integrity": "sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/typescript-estree": "8.32.1",
- "@typescript-eslint/utils": "8.32.1",
- "debug": "^4.3.4",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/types": "8.52.0",
+ "@typescript-eslint/visitor-keys": "8.52.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6761,16 +6759,12 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
}
},
- "node_modules/@typescript-eslint/types": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz",
- "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==",
+ "node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz",
+ "integrity": "sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==",
"dev": true,
"license": "MIT",
"engines": {
@@ -6779,23 +6773,23 @@
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz",
- "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==",
+ "node_modules/@typescript-eslint/type-utils": {
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz",
+ "integrity": "sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.32.1",
- "@typescript-eslint/visitor-keys": "8.32.1",
- "debug": "^4.3.4",
- "fast-glob": "^3.3.2",
- "is-glob": "^4.0.3",
- "minimatch": "^9.0.4",
- "semver": "^7.6.0",
- "ts-api-utils": "^2.1.0"
+ "@typescript-eslint/types": "8.52.0",
+ "@typescript-eslint/typescript-estree": "8.52.0",
+ "@typescript-eslint/utils": "8.52.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6805,47 +6799,50 @@
"url": "https://opencollective.com/typescript-eslint"
},
"peerDependencies": {
- "typescript": ">=4.8.4 <5.9.0"
+ "eslint": "^8.57.0 || ^9.0.0",
+ "typescript": ">=4.8.4 <6.0.0"
}
},
- "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+ "node_modules/@typescript-eslint/types": {
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz",
+ "integrity": "sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==",
"dev": true,
"license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@typescript-eslint/typescript-estree/node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz",
+ "integrity": "sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
+ "@typescript-eslint/project-service": "8.52.0",
+ "@typescript-eslint/tsconfig-utils": "8.52.0",
+ "@typescript-eslint/types": "8.52.0",
+ "@typescript-eslint/visitor-keys": "8.52.0",
+ "debug": "^4.4.3",
+ "minimatch": "^9.0.5",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.4.0"
},
"engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
},
- "engines": {
- "node": ">= 6"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": {
@@ -6865,16 +6862,16 @@
}
},
"node_modules/@typescript-eslint/utils": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz",
- "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==",
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz",
+ "integrity": "sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@eslint-community/eslint-utils": "^4.7.0",
- "@typescript-eslint/scope-manager": "8.32.1",
- "@typescript-eslint/types": "8.32.1",
- "@typescript-eslint/typescript-estree": "8.32.1"
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.52.0",
+ "@typescript-eslint/types": "8.52.0",
+ "@typescript-eslint/typescript-estree": "8.52.0"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6885,18 +6882,18 @@
},
"peerDependencies": {
"eslint": "^8.57.0 || ^9.0.0",
- "typescript": ">=4.8.4 <5.9.0"
+ "typescript": ">=4.8.4 <6.0.0"
}
},
"node_modules/@typescript-eslint/visitor-keys": {
- "version": "8.32.1",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz",
- "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==",
+ "version": "8.52.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz",
+ "integrity": "sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@typescript-eslint/types": "8.32.1",
- "eslint-visitor-keys": "^4.2.0"
+ "@typescript-eslint/types": "8.52.0",
+ "eslint-visitor-keys": "^4.2.1"
},
"engines": {
"node": "^18.18.0 || ^20.9.0 || >=21.1.0"
@@ -6907,9 +6904,9 @@
}
},
"node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz",
- "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==",
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz",
+ "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
@@ -7210,6 +7207,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
"integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -7219,6 +7217,7 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -7230,31 +7229,6 @@
"url": "https://github.com/chalk/ansi-styles?sponsor=1"
}
},
- "node_modules/any-promise": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz",
- "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==",
- "license": "MIT"
- },
- "node_modules/anymatch": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
- "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
- "license": "ISC",
- "dependencies": {
- "normalize-path": "^3.0.0",
- "picomatch": "^2.0.4"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/arg": {
- "version": "5.0.2",
- "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz",
- "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==",
- "license": "MIT"
- },
"node_modules/argparse": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
@@ -7480,13 +7454,13 @@
}
},
"node_modules/axios": {
- "version": "1.9.0",
- "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
- "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
+ "version": "1.13.2",
+ "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz",
+ "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==",
"license": "MIT",
"dependencies": {
"follow-redirects": "^1.15.6",
- "form-data": "^4.0.0",
+ "form-data": "^4.0.4",
"proxy-from-env": "^1.1.0"
}
},
@@ -7514,35 +7488,24 @@
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
"integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
"license": "MIT"
},
- "node_modules/binary-extensions": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
- "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
- "license": "MIT",
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/brace-expansion": {
- "version": "1.1.11",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
- "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz",
+ "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
+ "balanced-match": "^1.0.0"
}
},
"node_modules/braces": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
"integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"fill-range": "^7.1.1"
@@ -7551,17 +7514,6 @@
"node": ">=8"
}
},
- "node_modules/busboy": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
- "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
- "dependencies": {
- "streamsearch": "^1.1.0"
- },
- "engines": {
- "node": ">=10.16.0"
- }
- },
"node_modules/call-bind": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz",
@@ -7621,15 +7573,6 @@
"node": ">=6"
}
},
- "node_modules/camelcase-css": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz",
- "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==",
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/caniuse-lite": {
"version": "1.0.30001718",
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz",
@@ -7717,42 +7660,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/chokidar": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
- "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
- "license": "MIT",
- "dependencies": {
- "anymatch": "~3.1.2",
- "braces": "~3.0.2",
- "glob-parent": "~5.1.2",
- "is-binary-path": "~2.1.0",
- "is-glob": "~4.0.1",
- "normalize-path": "~3.0.0",
- "readdirp": "~3.6.0"
- },
- "engines": {
- "node": ">= 8.10.0"
- },
- "funding": {
- "url": "https://paulmillr.com/funding/"
- },
- "optionalDependencies": {
- "fsevents": "~2.3.2"
- }
- },
- "node_modules/chokidar/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
- },
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/client-only": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
@@ -7760,9 +7667,9 @@
"license": "MIT"
},
"node_modules/clsx": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz",
- "integrity": "sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==",
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
@@ -7837,58 +7744,31 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/commander": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz",
- "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==",
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/compute-scroll-into-view": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz",
"integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==",
"license": "MIT"
},
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/cross-spawn": {
"version": "7.0.6",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
"integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/cssesc": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz",
- "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==",
- "license": "MIT",
- "bin": {
- "cssesc": "bin/cssesc"
+ "which": "^2.0.1"
},
"engines": {
- "node": ">=4"
+ "node": ">= 8"
}
},
"node_modules/csstype": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
- "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "version": "3.2.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz",
+ "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==",
"license": "MIT"
},
"node_modules/d3-array": {
@@ -8074,9 +7954,9 @@
}
},
"node_modules/debug": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
- "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
"license": "MIT",
"dependencies": {
"ms": "^2.1.3"
@@ -8186,11 +8066,11 @@
}
},
"node_modules/detect-libc": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
- "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz",
+ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==",
+ "devOptional": true,
"license": "Apache-2.0",
- "optional": true,
"engines": {
"node": ">=8"
}
@@ -8208,18 +8088,6 @@
"url": "https://github.com/sponsors/wooorm"
}
},
- "node_modules/didyoumean": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz",
- "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==",
- "license": "Apache-2.0"
- },
- "node_modules/dlv": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz",
- "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==",
- "license": "MIT"
- },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -8251,14 +8119,30 @@
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
"license": "MIT"
},
"node_modules/emoji-regex": {
"version": "9.2.2",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
"integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
"license": "MIT"
},
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.4",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz",
+ "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
"node_modules/es-abstract": {
"version": "1.23.9",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz",
@@ -8510,13 +8394,13 @@
}
},
"node_modules/eslint-config-next": {
- "version": "15.0.2",
- "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.0.2.tgz",
- "integrity": "sha512-N8o6cyUXzlMmQbdc2Kc83g1qomFi3ITqrAZfubipVKET2uR2mCStyGRcx/r8WiAIVMul2KfwRiCHBkTpBvGBmA==",
+ "version": "15.5.9",
+ "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.9.tgz",
+ "integrity": "sha512-852JYI3NkFNzW8CqsMhI0K2CDRxTObdZ2jQJj5CtpEaOkYHn13107tHpNuD/h0WRpU4FAbCdUaxQsrfBtNK9Kw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@next/eslint-plugin-next": "15.0.2",
+ "@next/eslint-plugin-next": "15.5.9",
"@rushstack/eslint-patch": "^1.10.3",
"@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
"@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0",
@@ -8524,7 +8408,7 @@
"eslint-import-resolver-typescript": "^3.5.2",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.0",
- "eslint-plugin-react": "^7.35.0",
+ "eslint-plugin-react": "^7.37.0",
"eslint-plugin-react-hooks": "^5.0.0"
},
"peerDependencies": {
@@ -8537,6 +8421,19 @@
}
}
},
+ "node_modules/eslint-config-prettier": {
+ "version": "9.1.2",
+ "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz",
+ "integrity": "sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "eslint-config-prettier": "bin/cli.js"
+ },
+ "peerDependencies": {
+ "eslint": ">=7.0.0"
+ }
+ },
"node_modules/eslint-import-resolver-node": {
"version": "0.3.9",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz",
@@ -8976,6 +8873,7 @@
"version": "1.19.1",
"resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz",
"integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"reusify": "^1.0.4"
@@ -8998,6 +8896,7 @@
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
"integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"to-regex-range": "^5.0.1"
@@ -9094,6 +8993,7 @@
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
"integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"cross-spawn": "^7.0.6",
@@ -9107,14 +9007,15 @@
}
},
"node_modules/form-data": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
- "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
"mime-types": "^2.1.12"
},
"engines": {
@@ -9122,13 +9023,13 @@
}
},
"node_modules/framer-motion": {
- "version": "11.18.2",
- "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz",
- "integrity": "sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==",
+ "version": "12.24.7",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.24.7.tgz",
+ "integrity": "sha512-EolFLm7NdEMhWO/VTMZ0LlR4fLHGDiJItTx3i8dlyQooOOBoYAaysK4paGD4PrwqnoDdeDOS+TxnSBIAnNHs3w==",
"license": "MIT",
"dependencies": {
- "motion-dom": "^11.18.1",
- "motion-utils": "^11.18.1",
+ "motion-dom": "^12.24.3",
+ "motion-utils": "^12.23.28",
"tslib": "^2.4.0"
},
"peerDependencies": {
@@ -9148,27 +9049,6 @@
}
}
},
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/fsevents": {
- "version": "2.3.3",
- "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
- "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
- "hasInstallScript": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
- }
- },
"node_modules/function-bind": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
@@ -9278,22 +9158,21 @@
}
},
"node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Glob versions prior to v9 are no longer supported",
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
"dev": true,
"license": "ISC",
"dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
+ "foreground-child": "^3.1.0",
+ "jackspeak": "^3.1.2",
+ "minimatch": "^9.0.4",
+ "minipass": "^7.1.2",
+ "package-json-from-dist": "^1.0.0",
+ "path-scurry": "^1.11.1"
},
- "engines": {
- "node": "*"
+ "bin": {
+ "glob": "dist/esm/bin.mjs"
},
"funding": {
"url": "https://github.com/sponsors/isaacs"
@@ -9303,6 +9182,7 @@
"version": "6.0.2",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
"integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"is-glob": "^4.0.3"
@@ -9311,6 +9191,22 @@
"node": ">=10.13.0"
}
},
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "9.0.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
+ "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
"node_modules/globals": {
"version": "13.24.0",
"resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
@@ -9356,6 +9252,13 @@
"url": "https://github.com/sponsors/ljharb"
}
},
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
"node_modules/graphemer": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
@@ -9364,9 +9267,9 @@
"license": "MIT"
},
"node_modules/gsap": {
- "version": "3.13.0",
- "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz",
- "integrity": "sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==",
+ "version": "3.14.2",
+ "resolved": "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz",
+ "integrity": "sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==",
"license": "Standard 'no charge' license: https://gsap.com/standard-license."
},
"node_modules/hangul-js": {
@@ -9563,25 +9466,6 @@
"node": ">=0.8.19"
}
},
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC"
- },
"node_modules/inline-style-parser": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
@@ -9727,18 +9611,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-binary-path": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
- "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
- "license": "MIT",
- "dependencies": {
- "binary-extensions": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
"node_modules/is-boolean-object": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz",
@@ -9783,6 +9655,7 @@
"version": "2.16.1",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz",
"integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"hasown": "^2.0.2"
@@ -9843,6 +9716,7 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
"integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -9868,6 +9742,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -9896,6 +9771,7 @@
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
"integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"is-extglob": "^2.1.1"
@@ -9931,6 +9807,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
"integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.12.0"
@@ -10131,6 +10008,7 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
"license": "ISC"
},
"node_modules/iterator.prototype": {
@@ -10155,6 +10033,7 @@
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
"integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"@isaacs/cliui": "^8.0.2"
@@ -10167,12 +10046,13 @@
}
},
"node_modules/jiti": {
- "version": "1.21.7",
- "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
- "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
+ "version": "2.6.1",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz",
+ "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==",
+ "dev": true,
"license": "MIT",
"bin": {
- "jiti": "bin/jiti.js"
+ "jiti": "lib/jiti-cli.mjs"
}
},
"node_modules/js-tokens": {
@@ -10182,9 +10062,9 @@
"license": "MIT"
},
"node_modules/js-yaml": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
- "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
"dev": true,
"license": "MIT",
"dependencies": {
@@ -10224,94 +10104,337 @@
"dependencies": {
"minimist": "^1.2.0"
},
- "bin": {
- "json5": "lib/cli.js"
+ "bin": {
+ "json5": "lib/cli.js"
+ }
+ },
+ "node_modules/jsx-ast-utils": {
+ "version": "3.3.5",
+ "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
+ "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "array-includes": "^3.1.6",
+ "array.prototype.flat": "^1.3.1",
+ "object.assign": "^4.1.4",
+ "object.values": "^1.1.6"
+ },
+ "engines": {
+ "node": ">=4.0"
+ }
+ },
+ "node_modules/kdbush": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
+ "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==",
+ "license": "ISC"
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/language-subtag-registry": {
+ "version": "0.3.23",
+ "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
+ "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "dev": true,
+ "license": "CC0-1.0"
+ },
+ "node_modules/language-tags": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
+ "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "language-subtag-registry": "^0.3.20"
+ },
+ "engines": {
+ "node": ">=0.10"
+ }
+ },
+ "node_modules/levn": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+ "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "prelude-ls": "^1.2.1",
+ "type-check": "~0.4.0"
+ },
+ "engines": {
+ "node": ">= 0.8.0"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz",
+ "integrity": "sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-android-arm64": "1.30.2",
+ "lightningcss-darwin-arm64": "1.30.2",
+ "lightningcss-darwin-x64": "1.30.2",
+ "lightningcss-freebsd-x64": "1.30.2",
+ "lightningcss-linux-arm-gnueabihf": "1.30.2",
+ "lightningcss-linux-arm64-gnu": "1.30.2",
+ "lightningcss-linux-arm64-musl": "1.30.2",
+ "lightningcss-linux-x64-gnu": "1.30.2",
+ "lightningcss-linux-x64-musl": "1.30.2",
+ "lightningcss-win32-arm64-msvc": "1.30.2",
+ "lightningcss-win32-x64-msvc": "1.30.2"
+ }
+ },
+ "node_modules/lightningcss-android-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz",
+ "integrity": "sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz",
+ "integrity": "sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz",
+ "integrity": "sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz",
+ "integrity": "sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz",
+ "integrity": "sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/jsx-ast-utils": {
- "version": "3.3.5",
- "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
- "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==",
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz",
+ "integrity": "sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "MIT",
- "dependencies": {
- "array-includes": "^3.1.6",
- "array.prototype.flat": "^1.3.1",
- "object.assign": "^4.1.4",
- "object.values": "^1.1.6"
- },
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=4.0"
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/kdbush": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
- "integrity": "sha512-WbCVYJ27Sz8zi9Q7Q0xHC+05iwkm3Znipc2XTlrnJbsHMYktW4hPhXUE8Ys1engBrvffoSCqbil1JQAa7clRpA==",
- "license": "ISC"
- },
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz",
+ "integrity": "sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.1"
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/language-subtag-registry": {
- "version": "0.3.23",
- "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz",
- "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==",
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz",
+ "integrity": "sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "CC0-1.0"
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
},
- "node_modules/language-tags": {
- "version": "1.0.9",
- "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz",
- "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==",
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz",
+ "integrity": "sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==",
+ "cpu": [
+ "x64"
+ ],
"dev": true,
- "license": "MIT",
- "dependencies": {
- "language-subtag-registry": "^0.3.20"
- },
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"engines": {
- "node": ">=0.10"
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/levn": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
- "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz",
+ "integrity": "sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==",
+ "cpu": [
+ "arm64"
+ ],
"dev": true,
- "license": "MIT",
- "dependencies": {
- "prelude-ls": "^1.2.1",
- "type-check": "~0.4.0"
- },
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">= 0.8.0"
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/lilconfig": {
- "version": "3.1.3",
- "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz",
- "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==",
- "license": "MIT",
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.2",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz",
+ "integrity": "sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
"engines": {
- "node": ">=14"
+ "node": ">= 12.0.0"
},
"funding": {
- "url": "https://github.com/sponsors/antonk52"
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
}
},
- "node_modules/lines-and-columns": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
- "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
- "license": "MIT"
- },
"node_modules/locate-path": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -10361,17 +10484,28 @@
"version": "10.4.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
"integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
"license": "ISC"
},
"node_modules/lucide-react": {
- "version": "0.507.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.507.0.tgz",
- "integrity": "sha512-XfgE6gvAHwAtnbUvWiTTHx4S3VGR+cUJHEc0vrh9Ogu672I1Tue2+Cp/8JJqpytgcBHAB1FVI297W4XGNwc2dQ==",
+ "version": "0.562.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz",
+ "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
+ "node_modules/magic-string": {
+ "version": "0.30.21",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz",
+ "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.5"
+ }
+ },
"node_modules/markdown-table": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
@@ -10633,9 +10767,9 @@
}
},
"node_modules/mdast-util-to-hast": {
- "version": "13.2.0",
- "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
- "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+ "version": "13.2.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz",
+ "integrity": "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==",
"license": "MIT",
"dependencies": {
"@types/hast": "^3.0.0",
@@ -10691,6 +10825,7 @@
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
"integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 8"
@@ -11263,6 +11398,7 @@
"version": "4.0.8",
"resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz",
"integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"braces": "^3.0.3",
@@ -11320,24 +11456,25 @@
"version": "7.1.2",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
"integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">=16 || 14 >=14.17"
}
},
"node_modules/motion-dom": {
- "version": "11.18.1",
- "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz",
- "integrity": "sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==",
+ "version": "12.24.3",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.3.tgz",
+ "integrity": "sha512-ZjMZCwhTglim0LM64kC1iFdm4o+2P9IKk3rl/Nb4RKsb5p4O9HJ1C2LWZXOFdsRtp6twpqWRXaFKOduF30ntow==",
"license": "MIT",
"dependencies": {
- "motion-utils": "^11.18.1"
+ "motion-utils": "^12.23.28"
}
},
"node_modules/motion-utils": {
- "version": "11.18.1",
- "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz",
- "integrity": "sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==",
+ "version": "12.23.28",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.28.tgz",
+ "integrity": "sha512-0W6cWd5Okoyf8jmessVK3spOmbyE0yTdNKujHctHH9XdAE4QDuZ1/LjSXC68rrhsJU+TkzXURC5OdSWh9ibOwQ==",
"license": "MIT"
},
"node_modules/ms": {
@@ -11346,17 +11483,6 @@
"integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
"license": "MIT"
},
- "node_modules/mz": {
- "version": "2.7.0",
- "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz",
- "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==",
- "license": "MIT",
- "dependencies": {
- "any-promise": "^1.0.0",
- "object-assign": "^4.0.1",
- "thenify-all": "^1.0.0"
- }
- },
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
@@ -11399,15 +11525,13 @@
"license": "MIT"
},
"node_modules/next": {
- "version": "15.3.2",
- "resolved": "https://registry.npmjs.org/next/-/next-15.3.2.tgz",
- "integrity": "sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ==",
+ "version": "15.5.9",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.5.9.tgz",
+ "integrity": "sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==",
"license": "MIT",
"dependencies": {
- "@next/env": "15.3.2",
- "@swc/counter": "0.1.3",
+ "@next/env": "15.5.9",
"@swc/helpers": "0.5.15",
- "busboy": "1.6.0",
"caniuse-lite": "^1.0.30001579",
"postcss": "8.4.31",
"styled-jsx": "5.1.6"
@@ -11419,19 +11543,19 @@
"node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
},
"optionalDependencies": {
- "@next/swc-darwin-arm64": "15.3.2",
- "@next/swc-darwin-x64": "15.3.2",
- "@next/swc-linux-arm64-gnu": "15.3.2",
- "@next/swc-linux-arm64-musl": "15.3.2",
- "@next/swc-linux-x64-gnu": "15.3.2",
- "@next/swc-linux-x64-musl": "15.3.2",
- "@next/swc-win32-arm64-msvc": "15.3.2",
- "@next/swc-win32-x64-msvc": "15.3.2",
- "sharp": "^0.34.1"
+ "@next/swc-darwin-arm64": "15.5.7",
+ "@next/swc-darwin-x64": "15.5.7",
+ "@next/swc-linux-arm64-gnu": "15.5.7",
+ "@next/swc-linux-arm64-musl": "15.5.7",
+ "@next/swc-linux-x64-gnu": "15.5.7",
+ "@next/swc-linux-x64-musl": "15.5.7",
+ "@next/swc-win32-arm64-msvc": "15.5.7",
+ "@next/swc-win32-x64-msvc": "15.5.7",
+ "sharp": "^0.34.3"
},
"peerDependencies": {
"@opentelemetry/api": "^1.1.0",
- "@playwright/test": "^1.41.2",
+ "@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",
@@ -11489,33 +11613,16 @@
"node": "^10 || ^12 || >=14"
}
},
- "node_modules/normalize-path": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
- "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
}
},
- "node_modules/object-hash": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz",
- "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==",
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
"node_modules/object-inspect": {
"version": "1.13.4",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
@@ -11629,16 +11736,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
"node_modules/optionator": {
"version": "0.9.4",
"resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -11711,6 +11808,7 @@
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
"integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
"license": "BlueOak-1.0.0"
},
"node_modules/parent-module": {
@@ -11761,20 +11859,11 @@
"node": ">=8"
}
},
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11784,12 +11873,14 @@
"version": "1.0.7",
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
"integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
+ "dev": true,
"license": "MIT"
},
"node_modules/path-scurry": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
"integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
"license": "BlueOak-1.0.0",
"dependencies": {
"lru-cache": "^10.2.0",
@@ -11812,150 +11903,39 @@
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
- "license": "MIT",
- "engines": {
- "node": ">=8.6"
- },
- "funding": {
- "url": "https://github.com/sponsors/jonschlinkert"
- }
- },
- "node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/pirates": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz",
- "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==",
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/possible-typed-array-names": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
- "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/postcss": {
- "version": "8.5.3",
- "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
- "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/funding/github/npm/postcss"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
- "license": "MIT",
- "dependencies": {
- "nanoid": "^3.3.8",
- "picocolors": "^1.1.1",
- "source-map-js": "^1.2.1"
- },
- "engines": {
- "node": "^10 || ^12 || >=14"
- }
- },
- "node_modules/postcss-import": {
- "version": "15.1.0",
- "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz",
- "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==",
- "license": "MIT",
- "dependencies": {
- "postcss-value-parser": "^4.0.0",
- "read-cache": "^1.0.0",
- "resolve": "^1.1.7"
- },
- "engines": {
- "node": ">=14.0.0"
- },
- "peerDependencies": {
- "postcss": "^8.0.0"
- }
- },
- "node_modules/postcss-js": {
- "version": "4.0.1",
- "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz",
- "integrity": "sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==",
- "license": "MIT",
- "dependencies": {
- "camelcase-css": "^2.0.1"
- },
- "engines": {
- "node": "^12 || ^14 || >= 16"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
+ "node": ">=8.6"
},
- "peerDependencies": {
- "postcss": "^8.4.21"
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
}
},
- "node_modules/postcss-load-config": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz",
- "integrity": "sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==",
- "funding": [
- {
- "type": "opencollective",
- "url": "https://opencollective.com/postcss/"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/ai"
- }
- ],
+ "node_modules/possible-typed-array-names": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
+ "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==",
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "lilconfig": "^3.0.0",
- "yaml": "^2.3.4"
- },
"engines": {
- "node": ">= 14"
- },
- "peerDependencies": {
- "postcss": ">=8.0.9",
- "ts-node": ">=9.0.0"
- },
- "peerDependenciesMeta": {
- "postcss": {
- "optional": true
- },
- "ts-node": {
- "optional": true
- }
+ "node": ">= 0.4"
}
},
- "node_modules/postcss-nested": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz",
- "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==",
+ "node_modules/postcss": {
+ "version": "8.5.3",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz",
+ "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==",
+ "dev": true,
"funding": [
{
"type": "opencollective",
"url": "https://opencollective.com/postcss/"
},
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
{
"type": "github",
"url": "https://github.com/sponsors/ai"
@@ -11963,34 +11943,14 @@
],
"license": "MIT",
"dependencies": {
- "postcss-selector-parser": "^6.1.1"
- },
- "engines": {
- "node": ">=12.0"
- },
- "peerDependencies": {
- "postcss": "^8.2.14"
- }
- },
- "node_modules/postcss-selector-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
- "license": "MIT",
- "dependencies": {
- "cssesc": "^3.0.0",
- "util-deprecate": "^1.0.2"
+ "nanoid": "^3.3.8",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
},
"engines": {
- "node": ">=4"
+ "node": "^10 || ^12 || >=14"
}
},
- "node_modules/postcss-value-parser": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
- "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
- "license": "MIT"
- },
"node_modules/prelude-ls": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -12001,6 +11961,22 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/prop-types": {
"version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
@@ -12043,6 +12019,7 @@
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
"integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -12085,15 +12062,13 @@
}
},
"node_modules/react-error-boundary": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz",
- "integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.2.tgz",
+ "integrity": "sha512-yvWErn55ag/ywZEFqYpXYX9rxIDPIabXIX25F184KY3F5Szk2x/cVieOflw5R47ltN3KzWOw82Lmlb4vNjyn9A==",
"license": "MIT",
- "dependencies": {
- "@babel/runtime": "^7.12.5"
- },
"peerDependencies": {
- "react": ">=16.13.1"
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
}
},
"node_modules/react-icons": {
@@ -12178,32 +12153,14 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
- "node_modules/read-cache": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
- "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==",
- "license": "MIT",
- "dependencies": {
- "pify": "^2.3.0"
- }
- },
- "node_modules/readdirp": {
- "version": "3.6.0",
- "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
- "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
- "license": "MIT",
- "dependencies": {
- "picomatch": "^2.2.1"
- },
- "engines": {
- "node": ">=8.10.0"
- }
- },
"node_modules/recharts": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.1.2.tgz",
- "integrity": "sha512-vhNbYwaxNbk/IATK0Ki29k3qvTkGqwvCgyQAQ9MavvvBwjvKnMTswdbklJpcOAoMPN/qxF3Lyqob0zO+ZXkZ4g==",
+ "version": "3.6.0",
+ "resolved": "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz",
+ "integrity": "sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==",
"license": "MIT",
+ "workspaces": [
+ "www"
+ ],
"dependencies": {
"@reduxjs/toolkit": "1.x.x || 2.x.x",
"clsx": "^2.1.1",
@@ -12226,15 +12183,6 @@
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
- "node_modules/recharts/node_modules/clsx": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
- "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
@@ -12385,6 +12333,7 @@
"version": "1.22.10",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz",
"integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"is-core-module": "^2.16.0",
@@ -12425,6 +12374,7 @@
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
"integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==",
+ "dev": true,
"license": "MIT",
"engines": {
"iojs": ">=1.0.0",
@@ -12452,6 +12402,7 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
"integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+ "dev": true,
"funding": [
{
"type": "github",
@@ -12545,9 +12496,9 @@
}
},
"node_modules/semver": {
- "version": "7.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
- "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "version": "7.7.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz",
+ "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==",
"devOptional": true,
"license": "ISC",
"bin": {
@@ -12607,16 +12558,16 @@
}
},
"node_modules/sharp": {
- "version": "0.34.1",
- "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz",
- "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==",
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz",
+ "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==",
"hasInstallScript": true,
"license": "Apache-2.0",
"optional": true,
"dependencies": {
- "color": "^4.2.3",
- "detect-libc": "^2.0.3",
- "semver": "^7.7.1"
+ "@img/colour": "^1.0.0",
+ "detect-libc": "^2.1.2",
+ "semver": "^7.7.3"
},
"engines": {
"node": "^18.17.0 || ^20.3.0 || >=21.0.0"
@@ -12625,32 +12576,37 @@
"url": "https://opencollective.com/libvips"
},
"optionalDependencies": {
- "@img/sharp-darwin-arm64": "0.34.1",
- "@img/sharp-darwin-x64": "0.34.1",
- "@img/sharp-libvips-darwin-arm64": "1.1.0",
- "@img/sharp-libvips-darwin-x64": "1.1.0",
- "@img/sharp-libvips-linux-arm": "1.1.0",
- "@img/sharp-libvips-linux-arm64": "1.1.0",
- "@img/sharp-libvips-linux-ppc64": "1.1.0",
- "@img/sharp-libvips-linux-s390x": "1.1.0",
- "@img/sharp-libvips-linux-x64": "1.1.0",
- "@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
- "@img/sharp-libvips-linuxmusl-x64": "1.1.0",
- "@img/sharp-linux-arm": "0.34.1",
- "@img/sharp-linux-arm64": "0.34.1",
- "@img/sharp-linux-s390x": "0.34.1",
- "@img/sharp-linux-x64": "0.34.1",
- "@img/sharp-linuxmusl-arm64": "0.34.1",
- "@img/sharp-linuxmusl-x64": "0.34.1",
- "@img/sharp-wasm32": "0.34.1",
- "@img/sharp-win32-ia32": "0.34.1",
- "@img/sharp-win32-x64": "0.34.1"
+ "@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"
}
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"shebang-regex": "^3.0.0"
@@ -12663,6 +12619,7 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -12748,6 +12705,7 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
"integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
"license": "ISC",
"engines": {
"node": ">=14"
@@ -12791,18 +12749,11 @@
"dev": true,
"license": "MIT"
},
- "node_modules/streamsearch": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
- "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
- "engines": {
- "node": ">=10.0.0"
- }
- },
"node_modules/string-width": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
"integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"eastasianwidth": "^0.2.0",
@@ -12821,6 +12772,7 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -12835,12 +12787,14 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
"license": "MIT"
},
"node_modules/string-width/node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -12850,9 +12804,10 @@
}
},
"node_modules/string-width/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
@@ -12995,6 +12950,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -13008,6 +12964,7 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -13080,72 +13037,6 @@
}
}
},
- "node_modules/sucrase": {
- "version": "3.35.0",
- "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz",
- "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==",
- "license": "MIT",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.2",
- "commander": "^4.0.0",
- "glob": "^10.3.10",
- "lines-and-columns": "^1.1.6",
- "mz": "^2.7.0",
- "pirates": "^4.0.1",
- "ts-interface-checker": "^0.1.9"
- },
- "bin": {
- "sucrase": "bin/sucrase",
- "sucrase-node": "bin/sucrase-node"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- }
- },
- "node_modules/sucrase/node_modules/brace-expansion": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
- "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0"
- }
- },
- "node_modules/sucrase/node_modules/glob": {
- "version": "10.4.5",
- "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz",
- "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==",
- "license": "ISC",
- "dependencies": {
- "foreground-child": "^3.1.0",
- "jackspeak": "^3.1.2",
- "minimatch": "^9.0.4",
- "minipass": "^7.1.2",
- "package-json-from-dist": "^1.0.0",
- "path-scurry": "^1.11.1"
- },
- "bin": {
- "glob": "dist/esm/bin.mjs"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/sucrase/node_modules/minimatch": {
- "version": "9.0.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz",
- "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==",
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^2.0.1"
- },
- "engines": {
- "node": ">=16 || 14 >=14.17"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
"node_modules/supercluster": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz",
@@ -13172,6 +13063,7 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
"integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">= 0.4"
@@ -13181,94 +13073,33 @@
}
},
"node_modules/tailwind-merge": {
- "version": "2.5.4",
- "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz",
- "integrity": "sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==",
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz",
+ "integrity": "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==",
"license": "MIT",
"funding": {
"type": "github",
"url": "https://github.com/sponsors/dcastil"
}
},
- "node_modules/tailwind-variants": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.3.0.tgz",
- "integrity": "sha512-ho2k5kn+LB1fT5XdNS3Clb96zieWxbStE9wNLK7D0AV64kdZMaYzAKo0fWl6fXLPY99ffF9oBJnIj5escEl/8A==",
- "license": "MIT",
- "dependencies": {
- "tailwind-merge": "^2.5.4"
- },
- "engines": {
- "node": ">=16.x",
- "pnpm": ">=7.x"
- },
- "peerDependencies": {
- "tailwindcss": "*"
- }
- },
"node_modules/tailwindcss": {
- "version": "3.4.17",
- "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz",
- "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==",
- "license": "MIT",
- "dependencies": {
- "@alloc/quick-lru": "^5.2.0",
- "arg": "^5.0.2",
- "chokidar": "^3.6.0",
- "didyoumean": "^1.2.2",
- "dlv": "^1.1.3",
- "fast-glob": "^3.3.2",
- "glob-parent": "^6.0.2",
- "is-glob": "^4.0.3",
- "jiti": "^1.21.6",
- "lilconfig": "^3.1.3",
- "micromatch": "^4.0.8",
- "normalize-path": "^3.0.0",
- "object-hash": "^3.0.0",
- "picocolors": "^1.1.1",
- "postcss": "^8.4.47",
- "postcss-import": "^15.1.0",
- "postcss-js": "^4.0.1",
- "postcss-load-config": "^4.0.2",
- "postcss-nested": "^6.2.0",
- "postcss-selector-parser": "^6.1.2",
- "resolve": "^1.22.8",
- "sucrase": "^3.35.0"
- },
- "bin": {
- "tailwind": "lib/cli.js",
- "tailwindcss": "lib/cli.js"
- },
- "engines": {
- "node": ">=14.0.0"
- }
+ "version": "4.1.18",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz",
+ "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==",
+ "license": "MIT"
},
- "node_modules/tailwindcss/node_modules/fast-glob": {
- "version": "3.3.3",
- "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz",
- "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==",
+ "node_modules/tapable": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
+ "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==",
+ "dev": true,
"license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "^2.0.2",
- "@nodelib/fs.walk": "^1.2.3",
- "glob-parent": "^5.1.2",
- "merge2": "^1.3.0",
- "micromatch": "^4.0.8"
- },
"engines": {
- "node": ">=8.6.0"
- }
- },
- "node_modules/tailwindcss/node_modules/fast-glob/node_modules/glob-parent": {
- "version": "5.1.2",
- "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
- "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
- "license": "ISC",
- "dependencies": {
- "is-glob": "^4.0.1"
+ "node": ">=6"
},
- "engines": {
- "node": ">= 6"
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
}
},
"node_modules/text-table": {
@@ -13278,27 +13109,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/thenify": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz",
- "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==",
- "license": "MIT",
- "dependencies": {
- "any-promise": "^1.0.0"
- }
- },
- "node_modules/thenify-all": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz",
- "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==",
- "license": "MIT",
- "dependencies": {
- "thenify": ">= 3.1.0 < 4"
- },
- "engines": {
- "node": ">=0.8"
- }
- },
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
@@ -13306,14 +13116,14 @@
"license": "MIT"
},
"node_modules/tinyglobby": {
- "version": "0.2.13",
- "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
- "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
+ "version": "0.2.15",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz",
+ "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "fdir": "^6.4.4",
- "picomatch": "^4.0.2"
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.3"
},
"engines": {
"node": ">=12.0.0"
@@ -13323,11 +13133,14 @@
}
},
"node_modules/tinyglobby/node_modules/fdir": {
- "version": "6.4.4",
- "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
- "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
"dev": true,
"license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
"peerDependencies": {
"picomatch": "^3 || ^4"
},
@@ -13338,9 +13151,9 @@
}
},
"node_modules/tinyglobby/node_modules/picomatch": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz",
- "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==",
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
+ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13354,6 +13167,7 @@
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
"integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"is-number": "^7.0.0"
@@ -13383,9 +13197,9 @@
}
},
"node_modules/ts-api-utils": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz",
- "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==",
+ "version": "2.4.0",
+ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz",
+ "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==",
"dev": true,
"license": "MIT",
"engines": {
@@ -13395,12 +13209,6 @@
"typescript": ">=4.8.4"
}
},
- "node_modules/ts-interface-checker": {
- "version": "0.1.13",
- "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz",
- "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==",
- "license": "Apache-2.0"
- },
"node_modules/tsconfig-paths": {
"version": "3.15.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz",
@@ -13534,9 +13342,9 @@
}
},
"node_modules/typescript": {
- "version": "5.8.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
- "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
@@ -13718,9 +13526,9 @@
}
},
"node_modules/use-isomorphic-layout-effect": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz",
- "integrity": "sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==",
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz",
+ "integrity": "sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==",
"license": "MIT",
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -13757,12 +13565,6 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
- "node_modules/util-deprecate": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
- "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
- "license": "MIT"
- },
"node_modules/vfile": {
"version": "6.0.3",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
@@ -13817,6 +13619,7 @@
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
"license": "ISC",
"dependencies": {
"isexe": "^2.0.0"
@@ -13931,6 +13734,7 @@
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
"integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^6.1.0",
@@ -13949,6 +13753,7 @@
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
"integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-styles": "^4.0.0",
@@ -13966,12 +13771,14 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
"license": "MIT"
},
"node_modules/wrap-ansi-cjs/node_modules/string-width": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -13983,9 +13790,10 @@
}
},
"node_modules/wrap-ansi/node_modules/ansi-regex": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz",
- "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==",
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -13995,9 +13803,10 @@
}
},
"node_modules/wrap-ansi/node_modules/ansi-styles": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
- "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
"license": "MIT",
"engines": {
"node": ">=12"
@@ -14007,9 +13816,10 @@
}
},
"node_modules/wrap-ansi/node_modules/strip-ansi": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
- "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz",
+ "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==",
+ "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^6.0.1"
@@ -14021,25 +13831,6 @@
"url": "https://github.com/chalk/strip-ansi?sponsor=1"
}
},
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/yaml": {
- "version": "2.7.1",
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz",
- "integrity": "sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==",
- "license": "ISC",
- "bin": {
- "yaml": "bin.mjs"
- },
- "engines": {
- "node": ">= 14"
- }
- },
"node_modules/yocto-queue": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
diff --git a/package.json b/package.json
index 27839b4..7d25fa1 100644
--- a/package.json
+++ b/package.json
@@ -6,42 +6,56 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
- "lint": "next lint"
+ "lint": "next lint",
+ "format": "prettier --write .",
+ "format:check": "prettier --check ."
},
"dependencies": {
"@heroicons/react": "^2.2.0",
- "@heroui/scroll-shadow": "^2.3.6",
- "@heroui/system": "^2.4.7",
- "@heroui/theme": "^2.4.6",
+ "@heroui/scroll-shadow": "^2.3.19",
+ "@heroui/system": "^2.4.25",
+ "@heroui/theme": "^2.4.25",
"@nextui-org/react": "^2.6.11",
- "@react-google-maps/api": "^2.20.7",
+ "@react-google-maps/api": "^2.20.8",
"@splidejs/react-splide": "^0.7.12",
"@splidejs/splide": "^4.1.4",
"@splidejs/splide-extension-auto-scroll": "^0.5.3",
"@studio-freight/lenis": "^1.0.42",
- "axios": "^1.7.9",
- "framer-motion": "^11.16.1",
- "gsap": "^3.12.7",
+ "axios": "1.13.2",
+ "clsx": "^2.1.1",
+ "framer-motion": "^12.24.7",
+ "gsap": "^3.14.2",
"hangul-js": "^0.2.6",
- "lucide-react": "^0.507.0",
- "next": ">=15.2.3",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-error-boundary": "^6.0.0",
+ "lucide-react": "^0.562.0",
+ "next": "15.5.9",
+ "react": "18.3.1",
+ "react-dom": "18.3.1",
+ "react-error-boundary": "^6.0.2",
"react-icons": "^5.4.0",
"react-markdown": "^10.1.0",
- "recharts": "^3.1.2",
+ "recharts": "^3.6.0",
"remark-breaks": "^4.0.0",
"remark-gfm": "^4.0.1",
+ "tailwind-merge": "^3.4.0",
"type-hangul": "^0.2.4"
},
"devDependencies": {
+ "@tailwindcss/postcss": "^4.1.18",
"@types/node": "22.15.21",
- "@types/react": "19.1.5",
+ "@types/react": "^18.2.0",
"eslint": "^8",
- "eslint-config-next": "15.0.2",
+ "eslint-config-next": "15.5.9",
+ "eslint-config-prettier": "^9.1.0",
+ "eslint-plugin-react": "^7.37.5",
"postcss": "^8",
- "tailwindcss": "^3.4.1",
- "typescript": "^5.8.3"
+ "prettier": "^3.5.1",
+ "tailwindcss": "^4.1.18",
+ "typescript": "^5.9.3"
+ },
+ "overrides": {
+ "brace-expansion": "^2.0.2",
+ "form-data": "^4.0.4",
+ "glob": "^10.5.0",
+ "mdast-util-to-hast": "^13.2.1"
}
}
diff --git a/postcss.config.mjs b/postcss.config.mjs
index 1a69fd2..4ba0caf 100644
--- a/postcss.config.mjs
+++ b/postcss.config.mjs
@@ -1,8 +1,10 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
- tailwindcss: {},
+ '@tailwindcss/postcss': {},
},
};
export default config;
+
+
diff --git a/public/fonts/google-sans-flex/google-sans-flex.woff2 b/public/fonts/google-sans-flex/google-sans-flex.woff2
new file mode 100644
index 0000000..494777a
Binary files /dev/null and b/public/fonts/google-sans-flex/google-sans-flex.woff2 differ
diff --git a/public/icons/gdgocIcon/mobile.svg b/public/icons/gdgocIcon/mobile.svg
new file mode 100644
index 0000000..c204e76
--- /dev/null
+++ b/public/icons/gdgocIcon/mobile.svg
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/icons/gdgocIcon/pc.svg b/public/icons/gdgocIcon/pc.svg
new file mode 100644
index 0000000..3b8ef53
--- /dev/null
+++ b/public/icons/gdgocIcon/pc.svg
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/public/icons/logo/google-g.svg b/public/icons/logo/google-g.svg
new file mode 100644
index 0000000..27fb6a7
--- /dev/null
+++ b/public/icons/logo/google-g.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
diff --git a/public/icons/ui/download.svg b/public/icons/ui/download.svg
new file mode 100644
index 0000000..381716b
--- /dev/null
+++ b/public/icons/ui/download.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/public/icons/ui/file.svg b/public/icons/ui/file.svg
new file mode 100644
index 0000000..68624d5
--- /dev/null
+++ b/public/icons/ui/file.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/public/icons/ui/trash_can.svg b/public/icons/ui/trash_can.svg
new file mode 100644
index 0000000..bd2cb23
--- /dev/null
+++ b/public/icons/ui/trash_can.svg
@@ -0,0 +1,3 @@
+
+
+
diff --git a/public/images/recruit/core_pic.jpeg b/public/images/recruit/core_pic.jpeg
new file mode 100644
index 0000000..1e1225c
Binary files /dev/null and b/public/images/recruit/core_pic.jpeg differ
diff --git a/public/images/recruit/core_pic_m.jpg b/public/images/recruit/core_pic_m.jpg
new file mode 100644
index 0000000..9aa0d64
Binary files /dev/null and b/public/images/recruit/core_pic_m.jpg differ
diff --git a/src/app/_auth/signin/[social]/callback/page.jsx b/src/app/_auth/signin/[social]/callback/page.jsx
deleted file mode 100644
index 6d4c159..0000000
--- a/src/app/_auth/signin/[social]/callback/page.jsx
+++ /dev/null
@@ -1,7 +0,0 @@
-'use client';
-
-import { GoogleAuth } from '@/services/auth/signin/google/GoogleAuth';
-
-export default function Page() {
- return ;
-}
diff --git a/src/app/_auth/signin/layout.js b/src/app/_auth/signin/layout.js
deleted file mode 100644
index e7d2bb0..0000000
--- a/src/app/_auth/signin/layout.js
+++ /dev/null
@@ -1,16 +0,0 @@
-//import { Suspense } from "react";
-import Header2 from '@/components/ui/common/Header2';
-
-export const metadata = {
- title: "SignIn",
- description: "SignIn to your account",
-};
-
-export default function SignInLayout({ children }) {
- return (
-
-
- {children}
-
- );
-}
\ No newline at end of file
diff --git a/src/app/_auth/signin/page.jsx b/src/app/_auth/signin/page.jsx
deleted file mode 100644
index 43d60b2..0000000
--- a/src/app/_auth/signin/page.jsx
+++ /dev/null
@@ -1,146 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { useRouter } from 'next/navigation';
-import Image from 'next/image';
-
-import Header2 from '@/components/ui/common/Header2';
-import Loader from '@/components/ui/common/Loader';
-
-import AuthLogin from '@/components/auth/screen/AuthLogin';
-import AuthFindId from '@/components/auth/screen/AuthFindId';
-import AuthResetPassword from '@/components/auth/screen/AuthResetPassword';
-import AuthResetRequest from '@/components/auth/screen/AuthResetRequest';
-
-import { GoogleLogin } from '@/services/auth/signin/google/GoogleLogin';
-import { login } from '@/services/auth/signin/custom/CustomAuthApi';
-
-import { useAuth } from '@/hooks/useAuth';
-
-import loginBg from '@public/images/bgimg.png';
-
-export default function Page() {
- const router = useRouter();
- const { setAccessToken } = useAuth();
- const { handleGoogleLogin } = GoogleLogin();
-
- const [password, setPassword] = useState('');
- const [errors, setErrors] = useState([]);
- const [isRendering, setIsRendering] = useState(0);
- const [loading, setLoading] = useState(false);
-
- const handleBackToLogin = () => setIsRendering(0);
- const handleFindIdClick = () => setIsRendering(1);
- const handleResetPasswordClick = () => setIsRendering(2);
- const handleBackToResetRequest = () => setIsRendering(2);
- const handleResetPasswordNext = () => setIsRendering(3);
-
- const validatePassword = (password) => {
- const newErrors = [];
- if (password.length <= 0) {
- newErrors.push('비밀번호를 입력해주세요.');
- }
- return newErrors;
- };
-
- const onSubmit = async (e) => {
- e.preventDefault();
-
- const formData = Object.fromEntries(new FormData(e.currentTarget));
- const passwordErrors = validatePassword(password);
-
- if (passwordErrors.length > 0) {
- setErrors(passwordErrors);
- } else {
- setErrors([]);
-
- try {
- const { email, password } = formData;
- setLoading(true);
- const res = await login(email, password);
- const { exists, access_token } = res.data.data;
-
- if (!exists) {
- alert('아이디 혹은 비밀번호가 올바르지 않습니다.');
- setLoading(false);
- return;
- }
-
- setAccessToken(access_token);
- router.push('/main');
- } catch (error) {
- console.error('로그인 실패:', error);
- alert('로그인 중 오류가 발생했습니다.');
- setLoading(false);
- }
- }
- };
-
- return (
- <>
-
-
-
- {/* 로그인 화면 */}
-
-
- {/* 아이디 찾기 화면 */}
-
-
- {/* 비밀번호 재설정 화면 1 */}
-
-
- {/* 비밀번호 재설정 화면 2 */}
-
-
- >
- );
-}
diff --git a/src/app/_auth/signup/layout.js b/src/app/_auth/signup/layout.js
deleted file mode 100644
index 62a540b..0000000
--- a/src/app/_auth/signup/layout.js
+++ /dev/null
@@ -1,16 +0,0 @@
-//import { Suspense } from "react";
-import Header from "@/components/ui/common/Header";
-
-export const metadata = {
- title: "SignUp",
- description: "SignUp to your account",
-};
-
-export default function SignUpLayout({ children }) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/_auth/signup/page.jsx b/src/app/_auth/signup/page.jsx
deleted file mode 100644
index d926517..0000000
--- a/src/app/_auth/signup/page.jsx
+++ /dev/null
@@ -1,178 +0,0 @@
-"use client"
-
-import { useState, useEffect } from "react";
-import { useRouter } from "next/navigation";
-import { Button } from "@nextui-org/react";
-
-import ProfileInfoForm from "@/components/auth/signup/ProfileInfoForm";
-import AdditionalInfoForm from "@/components/auth/signup/AdditionalInfoForm";
-
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi.js';
-import usePasswordValidation from "@/hooks/usePasswordValidation";
-
-export default function Signup() {
- const router = useRouter();
- const { apiClient, handLeLogout }= useAuthenticatedApi();
-
- const [isLoading, setIsLoading] = useState(true);
-
- // 사용자 정보 상태
- const [name, setName] = useState("");
- const [email, setEmail] = useState("");
- const [password, setPassword] = useState("");
- const [confirmPassword, setConfirmPassword] = useState("");
- const [isEmailValid, setIsEmailValid] = useState(false);
- const [major, setMajor] = useState("");
- const [studentId, setStudentId] = useState("");
- const [phoneNumber, setPhoneNumber] = useState("");
-
- // 애니메이션 상태
- const [isAnimating, setIsAnimating] = useState(false);
- const [isRendering, setIsRendering] = useState(false);
-
- // 필드 비활성화 상태
- const [isNameDisabled, setIsNameDisabled] = useState(false);
- const [isEmailDisabled, setIsEmailDisabled] = useState(false);
-
- // 비밀번호 유효성 검사 훅 사용
- const { errors } = usePasswordValidation(password);
-
- useEffect(() => {
- // localStorage에서 signup_email과 signup_name 값을 확인
- if (typeof window !== 'undefined') {
- const storedEmail = localStorage.getItem('signup_email');
- const storedName = localStorage.getItem('signup_name');
-
- if (storedEmail) {
- setEmail(storedEmail);
- setIsEmailDisabled(true);
- }
-
- if (storedName) {
- setName(storedName);
- setIsNameDisabled(true);
- }
- }
-
- setIsLoading(false);
- }, []);
-
- // 첫 번째 화면에서 다음 버튼 클릭 시 처리
- const handleNext = () => {
- if (!name || !email || !password || !confirmPassword) {
- alert("모든 칸을 채워주세요.");
- return;
- }
- if (password !== confirmPassword) {
- alert("비밀번호가 일치하지 않습니다.");
- return;
- }
- if(email === "pwc2002"){
- setIsEmailValid(true);
- alert("이미 가입된 이메일입니다.");
- return;
- }
- if(errors.length > 0){
- alert("비밀번호 조건을 만족해주세요.");
- return;
- }
-
- // 화면 전환 애니메이션
- setIsRendering(true);
- setTimeout(() => {
- setIsAnimating(true);
- }, 100);
- };
-
- const setMajorInfo = (value) => {
- setMajor(value);
- };
-
- // 가입하기 버튼 클릭 시 처리
- const handleSignup = async () => {
- if (!major || !studentId || !phoneNumber) {
- alert("모든 칸을 채워주세요.");
- console.log(name,email,password,major,studentId,phoneNumber);
- return;
- }
-
- // 회원가입 정보 객체 생성
- const userinfo = {
- name: name,
- email: email,
- password: password,
- major: major,
- studentId: studentId,
- phoneNumber: phoneNumber
- }
-
- try {
- const res = await apiClient.post('/auth/signup', userinfo);
- console.log(res);
- } catch (error) {
- console.log(error);
- }
-
- // 홈으로 리디렉션
- router.push("/");
- };
-
- // 로딩 상태 처리
- if (isLoading) {
- return (
-
- );
- }
-
- return (
-
-
-
회원가입하기
-
-
-
- {/* 하단 버튼 영역 */}
-
- 0 || password !== confirmPassword)}
- >
- {isAnimating ? "가입하기" : "다음"}
-
-
-
-
- );
-}
diff --git a/src/app/_study/(dashboard)/admin/[id]/page.jsx b/src/app/_study/(dashboard)/admin/[id]/page.jsx
deleted file mode 100644
index adbb7fd..0000000
--- a/src/app/_study/(dashboard)/admin/[id]/page.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-'use client';
-
-import { useState } from "react";
-import { useParams } from 'next/navigation';
-
-import StudyDashboardNav from "@/components/study/ui/nav/StudyDashboardNav";
-import InfoArea from "@/components/study/dashboard/InfoArea";
-
-export default function AdminDetailDashboard() {
- const pathParams = useParams();
- const studyId = decodeURIComponent(pathParams.id);
-
- const [activeMenu, setActiveMenu] = useState('reviewApplication');
-
- // menu click handler
- const handleMenuClick = (menuId) => {
- setActiveMenu(menuId);
- };
-
- return (
- <>
- {/* 추후 StudyDashboardNav는 고정, InfoArea는 스크롤되도록 변경 */}
- {/* 또한 모바일 버전에는 dropDown으로 StudyDashboardNav 표기하는게 나을듯? */}
-
- >
- );
-};
\ No newline at end of file
diff --git a/src/app/_study/(dashboard)/admin/page.jsx b/src/app/_study/(dashboard)/admin/page.jsx
deleted file mode 100644
index 83e4a7e..0000000
--- a/src/app/_study/(dashboard)/admin/page.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-'use client';
-
-import { useState } from "react";
-
-import StudyDashboardNav from "@/components/study/ui/nav/StudyDashboardNav";
-import InfoArea from "@/components/study/dashboard/InfoArea";
-
-export default function AdminDashboard() {
- const [activeMenu, setActiveMenu] = useState('createdStudies');
-
- // menu click handler
- const handleMenuClick = (menuId) => {
- setActiveMenu(menuId);
- };
-
- return (
- <>
- {/* 추후 StudyDashboardNav는 고정, InfoArea는 스크롤되도록 변경 */}
- {/* 또한 모바일 버전에는 dropDown으로 StudyDashboardNav 표기하는게 나을듯? */}
-
- >
- );
-};
\ No newline at end of file
diff --git a/src/app/_study/(dashboard)/my/[id]/page.jsx b/src/app/_study/(dashboard)/my/[id]/page.jsx
deleted file mode 100644
index c57c595..0000000
--- a/src/app/_study/(dashboard)/my/[id]/page.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-'use client';
-
-import { useState } from "react";
-import { useParams } from 'next/navigation';
-
-import StudyDashboardNav from "@/components/study/ui/nav/StudyDashboardNav";
-import InfoArea from "@/components/study/dashboard/InfoArea";
-
-export default function MyDetailDashboard() {
- const pathParams = useParams();
- const studyId = decodeURIComponent(pathParams.id);
-
- const [activeMenu, setActiveMenu] = useState('studyDetail');
-
- // menu click handler
- const handleMenuClick = (menuId) => {
- setActiveMenu(menuId);
- };
-
- return (
- <>
- {/* 추후 StudyDashboardNav는 고정, InfoArea는 스크롤되도록 변경 */}
- {/* 또한 모바일 버전에는 dropDown으로 StudyDashboardNav 표기하는게 나을듯? */}
-
- >
- );
-};
\ No newline at end of file
diff --git a/src/app/_study/(dashboard)/my/page.jsx b/src/app/_study/(dashboard)/my/page.jsx
deleted file mode 100644
index ba4a6a3..0000000
--- a/src/app/_study/(dashboard)/my/page.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-'use client';
-
-import { useState } from "react";
-
-import StudyDashboardNav from "@/components/study/ui/nav/StudyDashboardNav";
-import InfoArea from "@/components/study/dashboard/InfoArea";
-
-export default function MyDashboard() {
- const [activeMenu, setActiveMenu] = useState('appliedStudies');
-
- // menu click handler
- const handleMenuClick = (menuId) => {
- setActiveMenu(menuId);
- };
-
- return (
- <>
- {/* 추후 StudyDashboardNav는 고정, InfoArea는 스크롤되도록 변경 */}
- {/* 또한 모바일 버전에는 dropDown으로 StudyDashboardNav 표기하는게 나을듯? */}
-
- >
- );
-};
\ No newline at end of file
diff --git a/src/app/_study/apply/[id]/page.jsx b/src/app/_study/apply/[id]/page.jsx
deleted file mode 100644
index a9981f1..0000000
--- a/src/app/_study/apply/[id]/page.jsx
+++ /dev/null
@@ -1,155 +0,0 @@
-'use client';
-
-import {useEffect, useState} from 'react';
-import { useRouter, useParams } from 'next/navigation';
-import Image from 'next/image';
-
-// hooks
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-
-// API Services
-import { useStudyDetail } from "@/services/study/useStudyDetail";
-
-import SubmitButton from "@/components/ui/button/SubmitButton";
-
-// Resources
-import gdgocIcon from '@public/icons/logo.png';
-
-export default function Apply() {
- const { apiClient } = useAuthenticatedApi();
- const router = useRouter();
- const pathParams = useParams();
- const studyId = decodeURIComponent(pathParams.id);
-
- // API: useStudyDetail
- const { studyDetail, isRecruiting, isApplied, error } = useStudyDetail(apiClient, studyId);
-
- useEffect(() => {
- if (studyDetail) {
- // Check if the study is recruiting
- if (isRecruiting === false) {
- alert("해당 스터디는 모집 중이 아닙니다.");
- router.push(`/study/detail/${encodeURIComponent(studyId)}`);
- return;
- }
-
- // Check if the user is already applied
- if (isApplied) {
- alert("이미 신청한 스터디입니다.");
- router.push(`/study/my`);
- return;
- }
- }
- }, [studyDetail, isRecruiting, isApplied, router, studyId]);
-
- // FormData
- const [formData, setFormData] = useState({
- introduce: "",
- activityTime: "",
- });
-
- // onChange for form inputs
- const handleChange = (e) => {
- setFormData({ ...formData, [e.target.name]: e.target.value });
- };
-
- // onSubmit for form
- const handleSubmit = async (e) => {
- e.preventDefault();
- try {
- await apiClient.post(`/study/${studyId}/attendee`, formData);
- alert("신청이 완료되었습니다!");
-
- router.push(`/study/my`);
- } catch (error) {
- console.error("error submitting form");
- alert("신청 중 오류가 발생했습니다. 다시 시도해주세요.");
- }
- };
-
- return (
- <>
-
-
-
-
-
- {studyDetail ? (
- <>
- {/* Form */}
-
- >
- ) : (
-
-
-
스터디 신청 정보를 불러올 수 없습니다.
-
URL을 다시 확인해주세요.
-
-
- )}
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/_study/create/page.jsx b/src/app/_study/create/page.jsx
deleted file mode 100644
index 5cc3324..0000000
--- a/src/app/_study/create/page.jsx
+++ /dev/null
@@ -1,331 +0,0 @@
-'use client';
-
-import { useState } from "react";
-import { useRouter } from 'next/navigation';
-import { Button } from "@nextui-org/react";
-import Image from 'next/image';
-
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-
-import SubmitButton from '@/components/ui/button/SubmitButton';
-
-export default function CreateStudy() {
- const { apiClient } = useAuthenticatedApi();
- const router = useRouter();
-
- const [imagePreview, setImagePreview] = useState(null);
-
- // current date
- const getCurrentDate = () => {
- const now = new Date();
- return now.toISOString().slice(0, 10);
- };
-
- // add time 00:00:00
- const addTime = (dateStr) => {
- if (!dateStr) return "";
- return new Date(dateStr).toISOString();
- };
-
- // DataFormat
- const [formData, setFormData] = useState({
- title: "",
- simpleIntroduce: "",
- activityIntroduce: "",
- creatorType: "PERSONAL",
- recruitStartDate: getCurrentDate(),
- recruitEndDate: "",
- activityStartDate: "",
- activityEndDate: "",
- expectedTime: "",
- expectedPlace: "",
- image: null
- });
-
- // onChange for form
- const handleChange = (e) => {
- const { name, value, type, files } = e.target;
-
- if (type === 'file' && files[0]) {
- setFormData({ ...formData, [name]: files[0] });
-
- const reader = new FileReader();
- reader.onloadend = () => {
- setImagePreview(reader.result);
- };
- reader.readAsDataURL(files[0]);
- } else {
- setFormData({ ...formData, [name]: value });
- }
- };
-
- // onSubmit
- const handleSubmit = async (e) => {
- e.preventDefault();
-
- try {
- const imageFile = formData.image;
- const s3key = `study`;
-
- const s3Form = new FormData();
- s3Form.append('file', imageFile);
- s3Form.append('s3key', s3key);
-
- const resS3 = await apiClient.post('/resource/image', s3Form, {
- headers: {
- 'Content-Type': 'multipart/form-data',
- },
- });
- const getS3Key = resS3?.data?.data?.s3Key;
- if (!getS3Key) console.error("Error: uploaded image's S3Key Not Found");
-
- const updateFormData = {
- ...formData,
- recruitStartDate: addTime(formData.recruitStartDate),
- recruitEndDate: addTime(formData.recruitEndDate),
- activityStartDate: addTime(formData.activityStartDate),
- activityEndDate: addTime(formData.activityEndDate),
- imagePath: getS3Key
- };
-
- await apiClient.post('/study', updateFormData);
-
- alert("스터디 개설이 완료되었습니다!");
- router.push(`/study/admin`);
- } catch (error) {
- console.error("Error during submission:");
- alert("신청 중 오류가 발생했습니다. 다시 시도해주세요.");
- }
- };
-
- return (
- <>
-
-
-
-
- 신규 자율 스터디 개설하기
-
-
-
-
-
-
- >
- );
-};
\ No newline at end of file
diff --git a/src/app/_study/detail/[id]/page.jsx b/src/app/_study/detail/[id]/page.jsx
deleted file mode 100644
index dd010a4..0000000
--- a/src/app/_study/detail/[id]/page.jsx
+++ /dev/null
@@ -1,147 +0,0 @@
-'use client';
-
-import { useState, useCallback } from "react";
-import { useRouter, useParams } from 'next/navigation';
-import { Button } from "@nextui-org/react";
-import Image from 'next/image';
-
-import SubmitButton from '@/components/ui/button/SubmitButton';
-
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-
-import { useStudyDetail } from '@/services/study/useStudyDetail';
-
-import { formatDate } from '@/utils/formatDate';
-
-import gdgocIcon from '@public/icons/logo.png';
-
-export default function Detail() {
- const { apiClient } = useAuthenticatedApi();
- const router = useRouter();
- const pathParams = useParams();
- const studyId = decodeURIComponent(pathParams.id);
-
- const [leadInfoToggle, setLeadInfoToggle] = useState(false);
-
- // API: useStudyDetail
- const { studyDetail, studyLead, isRecruiting, isApplied, error } = useStudyDetail(apiClient, studyId);
-
- // toggle lead detail
- const toggleLeadDetail = () => {
- setLeadInfoToggle(!leadInfoToggle);
- };
-
- // onClick to Apply
- const sendToApply = useCallback(() => {
- if (studyId) {
- router.push(`/study/apply/${encodeURIComponent(studyId)}`);
- }
- }, [router, studyId]);
-
- // onClick to MyPage
- const sendToMyPage = useCallback(() => {
- router.push(`/study/my/`);
- }, [router]);
-
- // Check if the user is already applied
- const renderSubmitButton = () => {
- if (!isRecruiting) return null;
-
- return (
-
- );
- };
-
- return (
- <>
-
-
- {/* White Box */}
-
-
-
- {studyDetail ? (
- <>
-
-
{studyDetail.simpleIntroduce}
-
-
-
-
모집 기간: {studyDetail.recruitStartDate ? formatDate(studyDetail.recruitStartDate) : '정보 없음'} ~ {studyDetail.recruitEndDate ? formatDate(studyDetail.recruitEndDate) : '정보 없음'}
-
활동 기간: {studyDetail.activityStartDate ? formatDate(studyDetail.activityStartDate) : '정보 없음'} ~ {studyDetail.activityEndDate ? formatDate(studyDetail.activityEndDate) : '정보 없음'}
-
-
-
-
{studyDetail.activityIntroduce}
-
-
-
-
장소: {studyDetail.expectedPlace}
-
진행 시간: {studyDetail.expectedTime}
-
-
-
-
- {'▶'}
-
- 스터디장 {studyLead.name} (정보 펼치기)
-
-
- {leadInfoToggle && (
-
-
이름: {studyLead.name}
-
학번: {studyLead.studentId}
-
전공: {studyLead.major}
-
연락처: {studyLead.phoneNumber}
-
- )}
-
-
- {/* Apply to Study or move to myPage */}
- {renderSubmitButton()}
- >
- ) : (
-
-
스터디 정보를 불러올 수 없습니다.
-
URL을 다시 확인해주세요.
-
- )}
-
-
-
- >
- );
-};
\ No newline at end of file
diff --git a/src/app/_study/layout.js b/src/app/_study/layout.js
deleted file mode 100644
index 9dbd573..0000000
--- a/src/app/_study/layout.js
+++ /dev/null
@@ -1,18 +0,0 @@
-import { Suspense } from "react";
-
-import Loading from "./loading";
-import MenuHeader from "@/components/ui/common/MenuHeader";
-
-export const metadata = {
- title: "Study",
- description: "Study group management and participation platform",
-};
-
-export default function StudyLayout({ children }) {
- return (
- }>
-
- {children}
-
- );
-}
\ No newline at end of file
diff --git a/src/app/_study/loading.js b/src/app/_study/loading.js
deleted file mode 100644
index 3c219b1..0000000
--- a/src/app/_study/loading.js
+++ /dev/null
@@ -1,171 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-import Image from 'next/image';
-
-// resource
-import gdgocIcon from "@public/icons/logo.png";
-
-const LOADING_TEXTS = [
- '페이지를 불러오는 중',
- '데이터를 가져오는 중',
- '잠시만 기다려주세요',
- '거의 다 왔습니다'
-];
-
-export default function Loading() {
- const [loadingDots, setLoadingDots] = useState('.');
- const [loadingText, setLoadingText] = useState(LOADING_TEXTS[0]);
- const [imageError, setImageError] = useState(false);
-
- // 로딩 점(...)의 애니메이션
- useEffect(() => {
- const dotsInterval = setInterval(() => {
- setLoadingDots(dots => dots.length >= 3 ? '.' : dots + '.');
- }, 500);
-
- return () => clearInterval(dotsInterval);
- }, []);
-
- // 로딩 텍스트를 주기적으로 변경
- useEffect(() => {
- const textInterval = setInterval(() => {
- setLoadingText(current => {
- const currentIndex = LOADING_TEXTS.indexOf(current);
- const nextIndex = (currentIndex + 1) % LOADING_TEXTS.length;
- return LOADING_TEXTS[nextIndex];
- });
- }, 3000);
-
- return () => clearInterval(textInterval);
- }, []);
-
- return (
-
- {/* 아이콘 배경 */}
-
-
- {/* 로딩 박스 */}
-
-
- {/* 로딩 아이콘 */}
-
-
-
- {!imageError ? (
-
setImageError(true)}
- />
- ) : (
-
- GDGoC
-
- )}
-
-
- {/* 회전하는 로딩 원 */}
-
-
-
-
-
-
-
- {/* 로딩 텍스트 */}
-
- Loading
-
-
-
-
-
- {loadingText}{loadingDots}
-
-
-
- {/* 로딩 프로그레스 바 */}
-
-
- {/* 작은 로고 */}
-
-
-
-
-
-
- {/* CSS for animations */}
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/_study/page.jsx b/src/app/_study/page.jsx
deleted file mode 100644
index a7c6079..0000000
--- a/src/app/_study/page.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-'use client';
-
-import { useState } from 'react';
-import { useRouter } from 'next/navigation';
-
-import StudyTypeNav from '@/components/study/ui/nav/StudyTypeNav';
-import StudySection from '@/components/study/ui/table/StudySection';
-import RoundImageButton from '@/components/ui/button/RoundImageButton';
-
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-
-import { useStudyList } from '@/services/study/useStudyList';
-
-import writeIcon from '@public/icons/ui/pencil.png';
-
-export default function Study() {
- const { apiClient } = useAuthenticatedApi();
- const router = useRouter();
-
- const [creatorType, setCreatorType] = useState('PERSONAL');
-
- /**
- * @warning 현재 studyListGDGOC는 비활성화 되어 있습니다. 필요시 useStudyList에서 활성화 해주세요.
- * @warning 또한, 현제 StudyTypeNav는 "PERSONAL" 선택지만 지원합니다. 필요시 StudyTypeNav에서 활성화 해주세요.
- */
-
- // API: useStudyList
- const { studyListGDGOC, studyListPERSONAL, error } = useStudyList(apiClient);
-
- // Set the studyList based on creatorType
- const studyList = creatorType === 'GDGOC' ? (studyListGDGOC || []) : (studyListPERSONAL || []);
-
- // GDGOC or PERSONAL
- const handleCreatorTypeChange = (type) => {
- if (type !== creatorType) {
- setCreatorType(type);
- }
- };
-
- return (
- <>
-
-
- GDGoC Inha에서는 높은 수준의 멤버들과
-
-
- 다양한 스터디를 진행하고 있습니다.
-
-
-
- {/* Study Type Nav */}
-
-
- {/* StudyList by creatorType*/}
-
-
- {/* Create New PERSONAL Study */}
- router.push(`/study/create`)} />
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/admin/member-manager/layout.js b/src/app/admin/member-manager/layout.js
deleted file mode 100644
index a14d516..0000000
--- a/src/app/admin/member-manager/layout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import MenuHeader from '@/components/ui/common/MenuHeader';
-import ApiCodeGuard from '@/components/auth/ApiCodeGuard.jsx';
-
-export const metadata = {
- title: "Member Manager", description: "Admin management platform",
-};
-
-export default function AdminLayout({children}) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/admin/member-manager/page.jsx b/src/app/admin/member-manager/page.jsx
deleted file mode 100644
index 2ec3771..0000000
--- a/src/app/admin/member-manager/page.jsx
+++ /dev/null
@@ -1,413 +0,0 @@
-'use client';
-
-import React, {useCallback, useEffect, useMemo, useRef, useState} from 'react';
-import {
- Button,
- Chip,
- Select,
- SelectItem,
- Spinner,
- Table,
- TableBody,
- TableCell,
- TableColumn,
- TableHeader,
- TableRow
-} from '@nextui-org/react';
-
-import {useAuthenticatedApi} from '@/hooks/useAuthenticatedApi';
-import {useAuth} from '@/hooks/useAuth';
-
-import AdminTableTopContent from '@/components/admin/AdminTableTopContent';
-import AdminTableBottomContent from '@/components/admin/AdminTableBottomContent';
-
-/** 백엔드 enum과 일치하는 값(전송용) */
-const ROLE_OPTIONS = ['GUEST', 'MEMBER', 'CORE', 'LEAD', 'ORGANIZER', 'ADMIN'];
-const TEAM_ENUM_VALUES = ['BD', 'HR', 'TECH', 'PR_DESIGN', 'HQ'];
-
-/** 화면 표시용 라벨 */
-const TEAM_LABEL = {
- BD: 'BD', HR: 'HR', TECH: 'TECH', PR_DESIGN: 'PR/DESIGN', HQ: 'HQ',
-};
-
-const roleColor = (r) => ({
- ADMIN: 'danger', ORGANIZER: 'warning', LEAD: 'secondary', CORE: 'success', MEMBER: 'primary',
-}[r] || 'default');
-
-const ROLE_RANK = {GUEST: 0, MEMBER: 1, CORE: 2, LEAD: 3, ORGANIZER: 4, ADMIN: 5};
-const getRoleRank = (r) => ROLE_RANK[r] ?? -1;
-
-export default function AdminUsersPage() {
- const {apiClient} = useAuthenticatedApi();
- const {accessToken} = useAuth();
-
- const [rows, setRows] = useState([]); // [{id,name,major,studentId,email,userRole,team}]
- const [loading, setLoading] = useState(false);
- const [err, setErr] = useState('');
-
- // 검색/정렬/페이지
- const [searchValue, setSearchValue] = useState('');
- const [query, setQuery] = useState('');
- const [page, setPage] = useState(1);
- const rowsPerPage = 20;
- const [totalPages, setTotalPages] = useState(1);
- const [totalUsers, setTotalUsers] = useState(0);
-
- // 정렬
- const [sortDescriptor, setSortDescriptor] = useState({column: 'name', direction: 'ascending'});
- const [pendingSort, setPendingSort] = useState(sortDescriptor);
- const sortTimerRef = useRef(null);
-
- // 중복 요청 방지 키
- const lastQueryRef = useRef(null); // string key
-
- /** 내 정보(자기 자신 삭제/변경 방지용) */
- const me = useMemo(() => {
- if (!accessToken) return null;
- try {
- const p = JSON.parse(atob(accessToken.split('.')[1]));
- return {id: Number(p.id) || null};
- } catch {
- return null;
- }
- }, [accessToken]);
-
- /** 삭제 버튼 렌더링 권한(서버로 체크: LEAD 이상) */
- const [canRenderDelete, setCanRenderDelete] = useState(false);
-
- useEffect(() => {
- let alive = true;
-
- (async () => {
- try {
- const res = await apiClient.get('/auth/LEAD', {
- validateStatus: (s) => s === 200 || s === 204 || s === 401 || s === 403,
- headers: {Accept: 'application/json'},
- });
-
- if (!alive) return;
-
- // 403이면 바로 discard
- if (res?.status === 403) {
- setCanRenderDelete(false);
- return;
- }
-
- const okHttp = res?.status === 200 || res?.status === 204;
- const okBody = (res?.data?.code ?? 200) === 200;
-
- setCanRenderDelete(okHttp && okBody);
- } catch {
- if (alive) setCanRenderDelete(false);
- }
- })();
-
- return () => {
- alive = false;
- };
- }, [apiClient]);
-
- // 프론트 → 백엔드 정렬 필드 매핑
- const sortColMap = useMemo(() => ({
- name: 'name',
- major: 'major',
- studentId: 'studentId',
- email: 'email',
- userRole: 'userRole',
- team: 'team',
- createdAt: 'createdAt', // 백업
- }), []);
-
- const handleSortChange = useCallback((d) => {
- setPendingSort((prev) => {
- const isNewColumn = prev.column !== d.column;
- let nextDirection = d.direction;
-
- if (isNewColumn) {
- if (d.column === 'userRole') nextDirection = 'descending';
- // 새 컬럼이면 페이지 리셋
- setPage(1);
- }
- return {column: d.column, direction: nextDirection};
- });
- }, [setPage]);
-
- const fetchUsers = useCallback(async (force = false) => {
- setErr('');
- const myKey = JSON.stringify({page, q: query, sort: sortDescriptor});
- if (!force && lastQueryRef.current === myKey) return;
- lastQueryRef.current = myKey;
-
- setLoading(true);
- try {
- const sort = sortColMap[sortDescriptor.column] || 'name';
- const dir = sortDescriptor.direction === 'descending' ? 'DESC' : 'ASC';
-
- const params = {
- page: page - 1, size: rowsPerPage, sort, dir, q: query || undefined,
- };
-
- const res = await apiClient.get('/admin/users', {params});
- const pageData = res?.data?.data;
- const meta = res?.data?.meta;
-
- let content = Array.isArray(pageData?.content) ? pageData.content : [];
-
- if (sortDescriptor.column === 'userRole') {
- const asc = sortDescriptor.direction !== 'descending';
- content = content.slice().sort((a, b) => {
- const diff = getRoleRank(a.userRole) - getRoleRank(b.userRole);
- return asc ? diff : -diff;
- });
- }
-
- if (lastQueryRef.current !== myKey) return;
-
- setRows(content);
-
- const total = meta?.totalElements ?? pageData?.totalElements ?? content.length;
- setTotalUsers(total);
- setTotalPages(Math.max(1, Math.ceil(total / rowsPerPage)));
- } catch (e) {
- setErr(e?.response?.data?.message || e?.message || '사용자 목록을 불러오지 못했습니다.');
- setRows([]);
- setTotalUsers(0);
- setTotalPages(1);
- } finally {
- setLoading(false);
- }
- }, [apiClient, page, rowsPerPage, query, sortDescriptor, sortColMap]);
-
- const didInitRef = useRef(false);
-
- useEffect(() => {
- if (didInitRef.current) return;
- didInitRef.current = true;
- fetchUsers(true);
- }, [fetchUsers]);
-
- useEffect(() => {
- fetchUsers();
- }, [fetchUsers]);
-
- // 테이블 정렬 디바운스 -> 실제 sortDescriptor 적용
- useEffect(() => {
- if (sortTimerRef.current) clearTimeout(sortTimerRef.current);
- sortTimerRef.current = setTimeout(() => {
- setSortDescriptor(pendingSort);
- }, 250);
- return () => clearTimeout(sortTimerRef.current);
- }, [pendingSort]);
-
- const onSearch = useCallback(() => {
- setPage(1);
- setQuery((searchValue || '').trim());
- }, [searchValue]);
-
- useEffect(() => {
- if (searchValue === '' && query !== '') {
- setPage(1);
- setQuery('');
- }
- }, [searchValue, query]);
-
- // 역할/팀 패치: role만 → /role, 팀만/둘다 → /role-team
- const patchSmart = useCallback(async ({user, nextRole, nextTeam}) => {
- const prev = rows;
- setRows((old) => old.map((u) => (u.id === user.id ? {...u, userRole: nextRole, team: nextTeam} : u)));
- try {
- const roleChanged = nextRole !== user.userRole;
- const teamChanged = (nextTeam ?? null) !== (user.team ?? null);
-
- if (teamChanged) {
- await apiClient.patch(`/admin/users/${user.id}/role-team`, {role: nextRole, team: nextTeam ?? null});
- } else if (roleChanged) {
- await apiClient.patch(`/admin/users/${user.id}/role`, {role: nextRole});
- }
- } catch (e) {
- setRows(prev); // 롤백
- alert(e?.response?.data?.message || '변경 실패');
- }
- }, [apiClient, rows]);
-
- // 삭제
- const handleDelete = useCallback(async (user) => {
- const confirmed = window.confirm(`{${user.name}}을(를) 삭제하시겠습니까?`);
- if (!confirmed) return;
-
- const prev = rows;
- // 낙관적 제거
- setRows((old) => old.filter((u) => u.id !== user.id));
-
- try {
- await apiClient.delete(`/admin/users/${user.id}`);
- } catch (e) {
- // 롤백
- setRows(prev);
- alert(e?.response?.data?.message || '삭제 실패');
- }
- }, [apiClient, rows]);
-
- // 6개 데이터 컬럼 + ACTIONS
- const columns = useMemo(() => {
- const base = [{name: 'NAME', uid: 'name', sortable: true}, {
- name: 'MAJOR', uid: 'major', sortable: true
- }, {name: 'STUDENT ID', uid: 'studentId', sortable: true}, {
- name: 'EMAIL', uid: 'email', sortable: true
- }, {name: 'ROLE', uid: 'userRole', sortable: true}, {name: 'TEAM', uid: 'team', sortable: true},];
- if (canRenderDelete) {
- base.push({name: 'ACTIONS', uid: 'actions', sortable: false});
- }
- return base;
- }, [canRenderDelete]);
-
- return (
-
사용자 관리
-
- {/* 검색바 */}
-
-
-
}
- >
-
- {(col) => (
- {col.name}
- )}
-
-
- }
- emptyContent={err || '데이터가 없습니다.'}
- >
- {(user) => {
- const cells = [// NAME
- (
- {user.name}
-
- {user.userRole}
-
- ),
-
- // MAJOR
- {user.major} ,
-
- // STUDENT ID
- {user.studentId} ,
-
- // EMAIL
- (
- {user.email}
- ),
-
- // ROLE
- (
-
- {
- const nextRole = String(Array.from(keys)[0] || user.userRole);
- if (nextRole !== user.userRole) {
- const ok = confirm(`역할을 '${user.userRole}' → '${nextRole}' 로 변경할까요?`);
- if (ok) void patchSmart({user, nextRole, nextTeam: user.team ?? null});
- }
- }}
- size="sm"
- className="min-w-[140px]"
- classNames={{
- trigger: 'bg-zinc-900 text-white border border-zinc-700 data-[hover=true]:bg-zinc-800',
- value: 'text-white',
- popoverContent: 'bg-zinc-900 border border-zinc-700',
- listbox: 'text-white',
- selectorIcon: 'text-zinc-400',
- }}
- itemClasses={{
- base: 'rounded-md data-[hover=true]:bg-zinc-800 data-[focus=true]:bg-zinc-800',
- title: 'text-white',
- }}
- >
- {ROLE_OPTIONS.map((r) => (
- {r}
- ))}
-
-
- ),
-
- // TEAM
- (
- {
- const k = String(Array.from(keys)[0] ?? '');
- const nextTeam = k === '' ? null : k; // 서버에는 enum name 전송
- if ((nextTeam ?? null) !== (user.team ?? null)) {
- const old = user.team ? TEAM_LABEL[user.team] || user.team : '(없음)';
- const neu = nextTeam ? TEAM_LABEL[nextTeam] || nextTeam : '(없음)';
- const ok = confirm(`팀을 '${old}' → '${neu}' 로 변경할까요?`);
- if (ok) void patchSmart({user, nextRole: user.userRole, nextTeam});
- }
- }}
- size="sm"
- className="min-w-[160px]"
- classNames={{
- trigger: 'bg-zinc-900 text-white border border-zinc-700 data-[hover=true]:bg-zinc-800',
- value: 'text-white',
- popoverContent: 'bg-zinc-900 border border-zinc-700',
- listbox: 'text-white',
- selectorIcon: 'text-zinc-400',
- }}
- itemClasses={{
- base: 'rounded-md data-[hover=true]:bg-zinc-800 data-[focus=true]:bg-zinc-800',
- title: 'text-white',
- }}
- >
-
- (없음)
-
- {TEAM_ENUM_VALUES.map((t) => (
- {TEAM_LABEL[t]}
- ))}
-
- ),
-
- // ACTIONS (조건부)
- canRenderDelete && (
-
- {user.id !== me?.id ? ( {
- e.stopPropagation();
- void handleDelete(user);
- }}
- >
- 삭제
- ) : null}
-
- ),].filter(Boolean);
-
- return (
- {cells}
- );
- }}
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/admin/recruit-manager/layout.js b/src/app/admin/recruit-manager/layout.js
deleted file mode 100644
index 41d8049..0000000
--- a/src/app/admin/recruit-manager/layout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import MenuHeader from '@/components/ui/common/MenuHeader';
-import ApiCodeGuard from '@/components/auth/ApiCodeGuard.jsx';
-
-export const metadata = {
- title: "Recruit Manager", description: "Admin management and participation platform",
-};
-
-export default function AdminLayout({children}) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/admin/recruit-manager/page.jsx b/src/app/admin/recruit-manager/page.jsx
deleted file mode 100644
index 8b7903e..0000000
--- a/src/app/admin/recruit-manager/page.jsx
+++ /dev/null
@@ -1,295 +0,0 @@
-'use client';
-
-import React, { useCallback, useRef, useEffect, useState } from 'react';
-import { useRouter } from 'next/navigation';
-import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell } from '@nextui-org/react';
-
-import UserDetailsModal from '@/components/admin/UserDetailsModal';
-import AdminTableCell from '@/components/admin/AdminTableCell';
-import AdminTableTopContent from '@/components/admin/AdminTableTopContent';
-import AdminTableBottomContent from '@/components/admin/AdminTableBottomContent';
-import AdminDashboard from '@/components/admin/AdminDashboard';
-
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-
-const columns = [
- { name: 'NAME', uid: 'name', sortable: true },
- { name: 'MAJOR / ID', uid: 'major', sortable: true },
- { name: 'PAYMENT', uid: 'isPayed', sortable: true },
- { name: 'TOGGLE', uid: 'togglePay' },
-];
-
-export default function Page() {
- const router = useRouter();
- const { apiClient } = useAuthenticatedApi();
-
- const [page, setPage] = React.useState(1);
-
- const [modalOpen, setModalOpen] = React.useState(false); // 모달 열림 상태
- const modalClosing = useRef(false); // 모달이 닫히는 상태를 추적
-
- const [selectedUser, setSelectedUser] = React.useState(null); // 선택된 사용자 데이터
- const [searchValue, setSearchValue] = React.useState(''); // 검색 입력 상태
- const [query, setQuery] = React.useState(''); // API 호출시 검색 내용
- const [loading, setLoading] = React.useState(false);
- const [error, setError] = React.useState('');
- const [currentUsers, setCurrentUsers] = React.useState([]);
- const [totalUsers, setTotalUsers] = React.useState(0);
- const [totalPages, setTotalPages] = React.useState(0);
-
- // 대시보드 통계용 전체 멤버 데이터
- const [statsUsers, setStatsUsers] = React.useState([]);
- const [statsTotal, setStatsTotal] = React.useState(0);
- const [statsLoading, setStatsLoading] = React.useState(false);
- const [statsError, setStatsError] = React.useState('');
- const [sortDescriptor, setSortDescriptor] = React.useState({
- column: 'createdAt',
- direction: 'descending',
- });
-
- const rowsPerPage = 10; //한 페이지당 표시될 유저 수
-
- //유저 데이터 조회
- const fetchUsers = useCallback(async () => {
- setLoading(true);
- setError('');
- try {
- // 정렬 컬럼 매핑 (프론트엔드 컬럼명 -> API 파라미터명)
- const sortColumnMap = {
- 'isPayed': 'isPayed',
- 'name': 'name',
- 'major': 'major',
- 'createdAt': 'createdAt',
- };
-
- const apiSortColumn = sortColumnMap[sortDescriptor.column] || 'createdAt';
- const apiSortDirection = sortDescriptor.direction === 'ascending' ? 'ASC' : 'DESC';
-
- const params = {
- page: page - 1,
- size: rowsPerPage,
- sort: apiSortColumn,
- dir: apiSortDirection,
- question: query || undefined,
- };
- const res = await apiClient.get('/recruit/members', { params });
- const list = Array.isArray(res?.data?.data) ? res.data.data : [];
- const total = res?.data?.meta?.totalElements ?? list.length;
- const computedTotalPages = Math.max(1, Math.ceil(total / rowsPerPage));
-
- setCurrentUsers(list);
- setTotalUsers(total);
- setTotalPages(computedTotalPages);
- } catch (err) {
- setError(String(err?.message || 'failed to load users'));
- setCurrentUsers([]);
- setTotalUsers(0);
- } finally {
- setLoading(false);
- }
- }, [apiClient, page, rowsPerPage, query, sortDescriptor]);
-
- // 통계용 전체 멤버 데이터 조회 (페이지네이션 병렬 수집)
- const fetchAllUsersForStats = useCallback(async () => {
- setStatsLoading(true);
- setStatsError('');
- try {
- const pageSize = 200;
- const baseParams = { size: pageSize, sort: 'createdAt', dir: 'DESC' };
-
- const firstRes = await apiClient.get('/recruit/members', { params: { ...baseParams, page: 0 } });
- const firstList = Array.isArray(firstRes?.data?.data) ? firstRes.data.data : [];
- const total = firstRes?.data?.meta?.totalElements ?? firstList.length;
- const totalPagesForStats = Math.max(1, Math.ceil(total / pageSize));
-
- let all = firstList;
- if (totalPagesForStats > 1) {
- const promises = [];
- for (let p = 1; p < totalPagesForStats; p++) {
- promises.push(apiClient.get('/recruit/members', { params: { ...baseParams, page: p } }));
- }
- const results = await Promise.allSettled(promises);
- results.forEach((res) => {
- if (res.status === 'fulfilled') {
- const list = Array.isArray(res.value?.data?.data) ? res.value.data.data : [];
- all = all.concat(list);
- }
- });
- }
-
- setStatsUsers(all);
- setStatsTotal(total);
- } catch (e) {
- setStatsUsers([]);
- setStatsTotal(0);
- setStatsError(String(e?.message || 'failed to load stats'));
- } finally {
- setStatsLoading(false);
- }
- }, [apiClient]);
-
- //회비 지불여부 체크박스
- const handleTogglePay = useCallback(
- async (userId, nextValue) => {
- if (modalClosing.current) return; // 모달이 닫히는 중에는 클릭 무시
- const confirmed = window.confirm('입금 상태를 수정하시겠습니까?');
- if (!confirmed) return;
- const getItemId = (user) => user?.id;
- const prevUsers = currentUsers;
-
- setCurrentUsers((prev) =>
- prev.map((u) => {
- if (getItemId(u) === userId) {
- const updated = { ...u, isPayed: nextValue };
- return updated;
- }
- return u;
- })
- );
-
- try {
- await apiClient.patch(`/recruit/members/${userId}/payment`, { isPayed: nextValue });
- } catch (err) {
- // rollback
- setCurrentUsers(prevUsers);
- alert('결제 상태 변경에 실패했습니다. 다시 시도해주세요.');
- }
- },
- [apiClient, currentUsers]
- );
-
- //테이블 요소
- const renderCell = useCallback(
- (user, columnKey) => {
- const normalizedUser = {
- ...user,
- name: user?.name ?? '',
- major: user?.major ?? '',
- studentId: user?.studentId ?? '',
- isPayed: typeof user?.isPayed === 'boolean' ? user.isPayed : '',
- phoneNumber: user?.phoneNumber ?? '',
- id: user?.id ?? user?.member?.id,
- memberId: user?.member?.id ?? user?.id,
- };
- return ;
- },
- [handleTogglePay]
- );
-
- //유저 상세 정보
- const handleRowClick = async (user) => {
- if (modalClosing.current) return; // 모달이 닫히는 중에는 클릭 무시
- try {
- const memberId = user?.id;
- if (!memberId) {
- throw new Error('멤버 ID를 확인할 수 없습니다.');
- }
- const res = await apiClient.get(`/recruit/members/${memberId}`);
- const detail = res?.data?.data ?? null;
- if (!detail) {
- throw new Error('상세 정보를 불러오지 못했습니다.');
- }
- setSelectedUser(detail);
- setModalOpen(true);
- } catch (e) {
- alert('상세 정보를 불러오는 중 오류가 발생했습니다.');
- }
- };
-
- const handleSearch = () => {
- setPage(1);
- setQuery((searchValue || '').trim());
- };
-
- const handleCloseModal = () => {
- modalClosing.current = true; // 모달이 닫히는 중임을 표시
- setModalOpen(false);
- setTimeout(() => {
- modalClosing.current = false; // 모달 닫힘 완료 후 상태 변경
- }, 300);
- };
-
- useEffect(() => {
- if (searchValue === '' && query !== '') {
- setPage(1);
- setQuery('');
- }
- }, [searchValue, query]);
-
- useEffect(() => {
- fetchUsers();
- }, [fetchUsers]);
-
- useEffect(() => {
- fetchAllUsersForStats();
- }, [fetchAllUsersForStats]);
-
- return (
- <>
-
-
- setPage(newPage)}
- />
-
- }
- topContent={
-
- }
- >
-
- {(column) => (
-
- {column.name}
-
- )}
-
-
- {(item) => (
- handleRowClick(item)}
- >
- {(columnKey) => (
-
- {renderCell(item, columnKey)}
-
- )}
-
- )}
-
-
-
-
-
- {/* 대시보드 */}
- {/* {statsError ? (
-
대시보드 로드 실패: {statsError}
- ) : statsLoading ? (
-
대시보드 불러오는 중...
- ) : (
-
- )} */}
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/api/auth/signin/route.ts b/src/app/api/auth/login/route.ts
similarity index 56%
rename from src/app/api/auth/signin/route.ts
rename to src/app/api/auth/login/route.ts
index de430a1..7831b14 100644
--- a/src/app/api/auth/signin/route.ts
+++ b/src/app/api/auth/login/route.ts
@@ -1,18 +1,18 @@
-import { NextResponse } from 'next/server';
-import axios from 'axios';
+import { NextResponse } from 'next/server'
+import axios from 'axios'
-import { rateLimit } from '@/lib/rate-limit';
+import { rateLimit } from '@/lib/rate-limit'
-const ORIGINAL_AUTH_URL = process.env.NEXT_PUBLIC_BASE_API_URL;
+const ORIGINAL_AUTH_URL = process.env.NEXT_PUBLIC_BASE_API_URL
interface LoginRequest {
- email: string;
- password: string;
+ email: string
+ password: string
}
interface LoginResponse {
- error?: string;
- [key: string]: any;
+ error?: string
+ [key: string]: any
}
export async function POST(request: Request): Promise {
@@ -20,94 +20,85 @@ export async function POST(request: Request): Promise {
// Rate limiting 적용
const limiter = rateLimit({
interval: 60 * 1000, // 1분
- uniqueTokenPerInterval: 500,
- });
+ uniqueTokenPerInterval: 500
+ })
try {
- await limiter.check(5, 'LOGIN_ATTEMPT'); // 1분당 5회 시도 제한
+ await limiter.check(5, 'LOGIN_ATTEMPT') // 1분당 5회 시도 제한
} catch {
return NextResponse.json(
{ error: '너무 많은 로그인 시도가 있었습니다. 잠시 후 다시 시도해주세요.' },
{ status: 429 }
- );
+ )
}
// 클라이언트로부터 받은 요청 데이터 추출
- const { email, password }: LoginRequest = await request.json();
+ const { email, password }: LoginRequest = await request.json()
// 입력값 검증
if (!email || !password) {
- return NextResponse.json(
- { error: '이메일과 비밀번호를 모두 입력해주세요.' },
- { status: 400 }
- );
+ return NextResponse.json({ error: '이메일과 비밀번호를 모두 입력해주세요.' }, { status: 400 })
}
if (!email.includes('@') || !email.includes('.')) {
- return NextResponse.json(
- { error: '유효한 이메일 주소를 입력해주세요.' },
- { status: 400 }
- );
+ return NextResponse.json({ error: '유효한 이메일 주소를 입력해주세요.' }, { status: 400 })
}
if (password.length < 8) {
- return NextResponse.json(
- { error: '비밀번호는 8자 이상이어야 합니다.' },
- { status: 400 }
- );
+ return NextResponse.json({ error: '비밀번호는 8자 이상이어야 합니다.' }, { status: 400 })
}
- const isProd = process.env.NODE_ENV === 'production';
+ const isProd = process.env.NODE_ENV === 'production'
// 기존 refresh_token 쿠키 삭제
- const response = NextResponse.json({});
+ const response = NextResponse.json({})
response.cookies.set('refresh_token', '', {
path: '/',
httpOnly: true,
secure: isProd,
sameSite: isProd ? 'none' : 'lax',
domain: isProd ? '.gdgocinha.com' : undefined,
- expires: new Date(0),
- });
+ expires: new Date(0)
+ })
const authResponse = await axios.post(
`${ORIGINAL_AUTH_URL}/auth/login`,
{ email, password },
{
headers: { 'Content-Type': 'application/json' },
- withCredentials: true,
+ withCredentials: true
}
- );
+ )
- const data = authResponse.data;
+ const data = authResponse.data
const nextResponse = NextResponse.json(data, {
status: authResponse.status,
- statusText: authResponse.statusText,
- });
+ statusText: authResponse.statusText
+ })
// 원본 응답의 쿠키가 있으면 추출하여 현재 도메인에 설정
- const cookies = authResponse.headers['set-cookie'];
+ const cookies = authResponse.headers['set-cookie']
if (cookies) {
cookies.forEach((cookie: string) => {
- const cookieParts = cookie.split(';')[0].split('=');
- const cookieName = cookieParts[0];
- const cookieValue = cookieParts.slice(1).join('=');
+ const cookieParts = cookie.split(';')[0].split('=')
+ const cookieName = cookieParts[0]
+ const cookieValue = cookieParts.slice(1).join('=')
nextResponse.cookies.set(cookieName, cookieValue, {
path: '/',
httpOnly: true,
secure: isProd,
sameSite: isProd ? 'none' : 'lax',
- domain: isProd ? '.gdgocinha.com' : undefined,
- });
- });
+ domain: isProd ? '.gdgocinha.com' : undefined
+ })
+ })
}
- return nextResponse;
+ return nextResponse
} catch (error: any) {
- console.error('로그인 프록시 오류:', error);
-
+ console.error('로그인 프록시 오류:', error)
+
// 구체적인 에러 메시지 처리
if (error.response) {
switch (error.response.status) {
@@ -115,28 +106,22 @@ export async function POST(request: Request): Promise {
return NextResponse.json(
{ error: '이메일 또는 비밀번호가 올바르지 않습니다.' },
{ status: 401 }
- );
+ )
case 403:
- return NextResponse.json(
- { error: '접근이 거부되었습니다.' },
- { status: 403 }
- );
+ return NextResponse.json({ error: '접근이 거부되었습니다.' }, { status: 403 })
case 404:
- return NextResponse.json(
- { error: '서비스를 찾을 수 없습니다.' },
- { status: 404 }
- );
+ return NextResponse.json({ error: '서비스를 찾을 수 없습니다.' }, { status: 404 })
default:
return NextResponse.json(
{ error: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.' },
{ status: error.response.status }
- );
+ )
}
}
return NextResponse.json(
{ error: '서버 오류가 발생했습니다. 잠시 후 다시 시도해주세요.' },
{ status: 500 }
- );
+ )
}
-}
\ No newline at end of file
+}
diff --git a/src/app/api/auth/refresh/route.ts b/src/app/api/auth/refresh/route.ts
index ea1f957..67ed031 100644
--- a/src/app/api/auth/refresh/route.ts
+++ b/src/app/api/auth/refresh/route.ts
@@ -6,7 +6,7 @@ const TIMEOUT = 5000;
interface RefreshResponse {
accessToken?: string;
- refreshToken?: string;
+ user?: Record;
message?: string;
}
@@ -20,11 +20,10 @@ export async function POST(req: NextRequest): Promise = await axios.post(
refreshUrl,
- body,
+ {},
{
headers: {
'Content-Type': 'application/json',
@@ -69,4 +68,4 @@ export async function POST(req: NextRequest): Promise {
try {
@@ -10,46 +10,46 @@ export async function POST(request: Request): Promise {
{},
{
headers: { 'Content-Type': 'application/json' },
- withCredentials: true,
+ withCredentials: true
}
- );
+ )
// 응답 생성
const nextResponse = NextResponse.json(
{ message: '로그아웃이 완료되었습니다.' },
{
status: response.status,
- statusText: response.statusText,
+ statusText: response.statusText
}
- );
+ )
// 쿠키 삭제
- const cookies = response.headers['set-cookie'];
+ const cookies = response.headers['set-cookie']
if (cookies) {
cookies.forEach((cookie: string) => {
- const cookieParts = cookie.split(';')[0].split('=');
- const cookieName = cookieParts[0];
+ const cookieParts = cookie.split(';')[0].split('=')
+ const cookieName = cookieParts[0]
// 쿠키 삭제
- nextResponse.cookies.delete(cookieName);
- });
+ nextResponse.cookies.delete(cookieName)
+ })
}
- nextResponse.cookies.delete('refresh_token');
+ nextResponse.cookies.delete('refresh_token')
- return nextResponse;
+ return nextResponse
} catch (error: any) {
- console.error('로그아웃 프록시 오류:', error);
+ console.error('로그아웃 프록시 오류:', error)
// 에러 응답 생성
const errorResponse = NextResponse.json(
{ error: '로그아웃 처리 중 오류가 발생했습니다.' },
{ status: error.response?.status || 500 }
- );
+ )
// 에러가 발생하더라도 클라이언트 측 쿠키는 삭제
- errorResponse.cookies.delete('refresh_token');
+ errorResponse.cookies.delete('refresh_token')
- return errorResponse;
+ return errorResponse
}
}
diff --git a/src/app/api/recruit/core/applications/route.js b/src/app/api/recruit/core/applications/route.js
new file mode 100644
index 0000000..d65e699
--- /dev/null
+++ b/src/app/api/recruit/core/applications/route.js
@@ -0,0 +1,36 @@
+import axios from 'axios'
+
+export async function POST(request) {
+ try {
+ const body = await request.json()
+ const authHeader = request.headers.get('authorization')
+
+ const response = await axios.post(
+ `${process.env.NEXT_PUBLIC_BASE_API_URL}/recruit/core/applications`,
+ body,
+ {
+ headers: {
+ 'Content-Type': 'application/json',
+ ...(authHeader && { Authorization: authHeader })
+ }
+ }
+ )
+
+ return new Response(JSON.stringify(response.data), {
+ status: response.status,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ })
+ } catch (error) {
+ const status = error.response?.status || 500
+ const data = error.response?.data || { message: 'Internal server error' }
+
+ return new Response(JSON.stringify(data), {
+ status: status,
+ headers: {
+ 'Content-Type': 'application/json'
+ }
+ })
+ }
+}
diff --git a/src/app/auth/signin/google/callback/page.jsx b/src/app/auth/signin/google/callback/page.jsx
deleted file mode 100644
index 6d4c159..0000000
--- a/src/app/auth/signin/google/callback/page.jsx
+++ /dev/null
@@ -1,7 +0,0 @@
-'use client';
-
-import { GoogleAuth } from '@/services/auth/signin/google/GoogleAuth';
-
-export default function Page() {
- return ;
-}
diff --git a/src/app/auth/signin/layout.js b/src/app/auth/signin/layout.js
deleted file mode 100644
index e7d2bb0..0000000
--- a/src/app/auth/signin/layout.js
+++ /dev/null
@@ -1,16 +0,0 @@
-//import { Suspense } from "react";
-import Header2 from '@/components/ui/common/Header2';
-
-export const metadata = {
- title: "SignIn",
- description: "SignIn to your account",
-};
-
-export default function SignInLayout({ children }) {
- return (
-
-
- {children}
-
- );
-}
\ No newline at end of file
diff --git a/src/app/auth/signin/page.jsx b/src/app/auth/signin/page.jsx
deleted file mode 100644
index a4fb929..0000000
--- a/src/app/auth/signin/page.jsx
+++ /dev/null
@@ -1,156 +0,0 @@
-'use client';
-
-import {useMemo, useState} from 'react';
-import {useRouter, useSearchParams} from 'next/navigation';
-import Image from 'next/image';
-import Loader from '@/components/ui/common/Loader';
-
-import AuthLogin from '@/components/auth/screen/AuthLogin';
-import AuthFindId from '@/components/auth/screen/AuthFindId';
-import AuthResetPassword from '@/components/auth/screen/AuthResetPassword';
-import AuthResetRequest from '@/components/auth/screen/AuthResetRequest';
-
-import {GoogleLogin} from '@/services/auth/signin/google/GoogleLogin';
-import {login} from '@/services/auth/signin/custom/CustomAuthApi';
-
-import {useAuth} from '@/hooks/useAuth';
-
-import loginBg from '@public/images/bgimg.png';
-
-export default function Page() {
- const router = useRouter();
- const searchParams = useSearchParams();
- const {setAccessToken} = useAuth();
- const {handleGoogleLogin} = GoogleLogin();
-
- // ✅ next 파라미터 처리 (없으면 /main)
- const nextUrl = useMemo(() => {
- const raw = searchParams?.get('next') || '';
- try {
- // next는 보통 encodeURIComponent 된 상태로 넘어오니 decode 시도
- const decoded = decodeURIComponent(raw);
- // 보안상 절대경로만 허용 (외부로 튈 수 있는 URL 차단)
- if (decoded.startsWith('/')) return decoded;
- return '/main';
- } catch {
- return '/main';
- }
- }, [searchParams]);
-
- const [password, setPassword] = useState('');
- const [errors, setErrors] = useState([]);
- const [isRendering, setIsRendering] = useState(0);
- const [loading, setLoading] = useState(false);
- const [verifiedEmail, setVerifiedEmail] = useState('');
-
- const handleBackToLogin = () => setIsRendering(0);
- const handleFindIdClick = () => setIsRendering(1);
- const handleResetPasswordClick = () => setIsRendering(2);
- const handleBackToResetRequest = () => setIsRendering(2);
- const handleResetPasswordNext = (email) => {
- setVerifiedEmail(email);
- setIsRendering(3);
- };
-
- const validatePassword = (password) => {
- const newErrors = [];
- if (password.length <= 0) {
- newErrors.push('비밀번호를 입력해주세요.');
- }
- return newErrors;
- };
-
- const onSubmit = async (e) => {
- e.preventDefault();
-
- const formData = Object.fromEntries(new FormData(e.currentTarget));
- const passwordErrors = validatePassword(password);
-
- if (passwordErrors.length > 0) {
- setErrors(passwordErrors);
- } else {
- setErrors([]);
-
- try {
- const {email, password} = formData;
- setLoading(true);
- const res = await login(email, password);
- const {exists, access_token} = res.data.data;
-
- if (!exists) {
- alert('아이디 혹은 비밀번호가 올바르지 않습니다.');
- setLoading(false);
- return;
- }
-
- setAccessToken(access_token);
-
- // ✅ 로그인 성공시 next로 이동
- router.push(nextUrl);
- } catch (error) {
- console.error('로그인 실패:', error);
- alert('로그인 중 오류가 발생했습니다.');
- setLoading(false);
- }
- }
- };
-
- return (<>
-
-
-
- {/* 로그인 화면 */}
-
-
handleGoogleLogin({next: nextUrl})}
- handleFindIdClick={handleFindIdClick}
- handleResetPasswordClick={handleResetPasswordClick}
- />
-
-
- {/* 아이디 찾기 화면 */}
-
-
- {/* 비밀번호 재설정 화면 1 */}
-
-
- {/* 비밀번호 재설정 화면 2 */}
-
-
- >);
-}
\ No newline at end of file
diff --git a/src/app/auth/signup/layout.js b/src/app/auth/signup/layout.js
deleted file mode 100644
index 62a540b..0000000
--- a/src/app/auth/signup/layout.js
+++ /dev/null
@@ -1,16 +0,0 @@
-//import { Suspense } from "react";
-import Header from "@/components/ui/common/Header";
-
-export const metadata = {
- title: "SignUp",
- description: "SignUp to your account",
-};
-
-export default function SignUpLayout({ children }) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/auth/signup/page.jsx b/src/app/auth/signup/page.jsx
deleted file mode 100644
index cf9014a..0000000
--- a/src/app/auth/signup/page.jsx
+++ /dev/null
@@ -1,198 +0,0 @@
-"use client"
-
-import { useState, useEffect } from "react";
-import { useRouter } from "next/navigation";
-import { Button } from "@nextui-org/react";
-
-import ProfileInfoForm from "@/components/auth/signup/ProfileInfoForm";
-import AdditionalInfoForm from "@/components/auth/signup/AdditionalInfoForm";
-
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi.js';
-import usePasswordValidation from "@/hooks/usePasswordValidation";
-
-export default function Signup() {
- const router = useRouter();
- const { apiClient, handLeLogout }= useAuthenticatedApi();
-
- const [isLoading, setIsLoading] = useState(true);
-
- // 사용자 정보 상태
- const [name, setName] = useState("");
- const [email, setEmail] = useState("");
- const [password, setPassword] = useState("");
- const [confirmPassword, setConfirmPassword] = useState("");
- const [isEmailValid, setIsEmailValid] = useState(false);
- const [major, setMajor] = useState("");
- const [studentId, setStudentId] = useState("");
- const [phoneNumber, setPhoneNumber] = useState("");
-
- // 애니메이션 상태
- const [isAnimating, setIsAnimating] = useState(false);
- const [isRendering, setIsRendering] = useState(false);
-
- // 필드 비활성화 상태
- const [isNameDisabled, setIsNameDisabled] = useState(false);
- const [isEmailDisabled, setIsEmailDisabled] = useState(false);
-
- // 비밀번호 유효성 검사 훅 사용
- const { errors } = usePasswordValidation(password);
-
- useEffect(() => {
- // localStorage에서 signup_email과 signup_name 값을 확인
- if (typeof window !== 'undefined') {
- const storedEmail = localStorage.getItem('signup_email');
- const storedName = localStorage.getItem('signup_name');
-
- if (storedEmail) {
- setEmail(storedEmail);
- setIsEmailDisabled(true);
- }
-
- if (storedName) {
- setName(storedName);
- setIsNameDisabled(true);
- }
- }
-
- setIsLoading(false);
- }, []);
-
- // 첫 번째 화면에서 다음 버튼 클릭 시 처리
- const handleNext = async () => {
- if (!name || !email || !password || !confirmPassword) {
- alert("모든 칸을 채워주세요.");
- return;
- }
- if (password !== confirmPassword) {
- alert("비밀번호가 일치하지 않습니다.");
- return;
- }
-
- const checkEmail = await apiClient.get('/auth/check', {
- params: {
- email: email
- }
- })
- if(checkEmail.data.data.isDuplicated){
- setIsEmailValid(true);
- return;
- }
-
- if(checkEmail.data.code != 200){
- alert("에러가 발생하였습니다.");
- return;
- }
-
-
-
- if(errors.length > 0){
- alert("비밀번호 조건을 만족해주세요.");
- return;
- }
-
- // 화면 전환 애니메이션
- setIsRendering(true);
- setTimeout(() => {
- setIsAnimating(true);
- }, 100);
- };
-
- const setMajorInfo = (value) => {
- setMajor(value);
- };
-
- // 가입하기 버튼 클릭 시 처리
- const handleSignup = async () => {
- if (!major || !studentId || !phoneNumber) {
- alert("모든 칸을 채워주세요.");
- console.log(name,email,password,major,studentId,phoneNumber);
- return;
- }
-
- // 회원가입 정보 객체 생성
- const userinfo = {
- name: name,
- email: email,
- password: password,
- major: major,
- studentId: studentId,
- phoneNumber: phoneNumber
- }
-
- try {
- const res = await apiClient.post('/auth/signup', userinfo);
- console.log(res);
- if(res.data.code == 200){
- console.log(res.data.message);
- router.push("/auth/signin");
- } else {
- alert(res.data.message);
- }
- } catch (error) {
- console.log(error);
- }
-
- // 홈으로 리디렉션
- router.push("/");
- };
-
- // 로딩 상태 처리
- if (isLoading) {
- return (
-
- );
- }
-
- return (
-
-
- {/*
회원가입하기
*/}
-
-
-
- {/* 하단 버튼 영역 */}
-
- 0 || password !== confirmPassword || isEmailValid)}
- >
- {isAnimating ? "가입하기" : "다음"}
-
-
-
-
- );
-}
diff --git a/src/app/core-attendance/layout.js b/src/app/core-attendance/layout.js
deleted file mode 100644
index 0722613..0000000
--- a/src/app/core-attendance/layout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import MenuHeader from "@/components/ui/common/MenuHeader";
-import ApiCodeGuard from "@/components/auth/ApiCodeGuard.jsx";
-
-export const metadata = {
- title: "Core Attendance", description: "GDGoC INHA Core Attendance Management",
-};
-
-export default function CoreAttendanceLayout({children}) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/core-attendance/page.jsx b/src/app/core-attendance/page.jsx
deleted file mode 100644
index d6e07d3..0000000
--- a/src/app/core-attendance/page.jsx
+++ /dev/null
@@ -1,477 +0,0 @@
-'use client';
-
-import {useEffect, useMemo, useRef, useState} from 'react';
-import {Button, Card, CardBody, Checkbox, Divider, Input, Select, SelectItem} from '@nextui-org/react';
-import {useAuthenticatedApi} from '@/hooks/useAuthenticatedApi';
-
-/** ===== 유틸 ===== */
-const ymd = (d = new Date()) => new Intl.DateTimeFormat('en-CA', {timeZone: 'Asia/Seoul'}).format(d);
-const getQS = (k) => (typeof window !== 'undefined' ? new URL(window.location.href).searchParams.get(k) || '' : '');
-const setQS = (entries) => {
- if (typeof window === 'undefined') return;
- const u = new URL(window.location.href);
- Object.entries(entries).forEach(([k, v]) => (v ? u.searchParams.set(k, v) : u.searchParams.delete(k)));
- window.history.replaceState({}, '', u.toString());
-};
-
-// ===== CSV 저장 공통 헬퍼 =====
-const saveResponseAsFile = (res, fallbackName) => {
- // Excel 한글 깨짐 방지용 BOM
- const BOM = new Uint8Array([0xEF, 0xBB, 0xBF]);
- const blob = new Blob([BOM, res.data], { type: 'text/csv;charset=utf-8' });
-
- // 서버가 파일명 내려주면 우선 사용
- let filename = fallbackName;
- const cd = res.headers && (res.headers['content-disposition'] || res.headers['Content-Disposition']);
- if (cd) {
- const m = /filename\*=UTF-8''([^;]+)|filename="?([^"]+)"?/i.exec(cd);
- const decoded = m && decodeURIComponent((m[1] || m[2] || '').trim());
- if (decoded) filename = decoded;
- }
-
- const url = URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = filename;
- document.body.appendChild(a);
- a.click();
- a.remove();
- URL.revokeObjectURL(url);
-};
-
-
-export default function AttendancePage() {
- const {apiClient} = useAuthenticatedApi();
-
- // URL state
- const [date, setDate] = useState(typeof window !== 'undefined' ? getQS('date') || ymd() : ymd());
-
- // data
- const [dates, setDates] = useState([]);
- const [teams, setTeams] = useState([]); // [{ id, name, lead? }]
- const [members, setMembers] = useState([]); // [{userId,name,team(=라벨),present,...}]
- const [summary, setSummary] = useState(null);
-
- // UI
- const [filter, setFilter] = useState('');
- const [teamFilter, setTeamFilter] = useState(''); // 팀 라벨 기준 (''=전체)
- const [presentSet, setPresentSet] = useState(new Set()); // Set
- const [dirty, setDirty] = useState(false);
-
- // 초기 상태(서버 로드 직후 present 사용자들) → Δ 저장용
- const initialPresentSetRef = useRef(new Set());
-
- /** ===== API 래퍼 ===== */
- const api = {
- getDates: async () => (await apiClient.get('/core-attendance/meetings')).data.data, // { dates: [...] }
- addDate: async (d) => (await apiClient.post('/core-attendance/meetings', {date: d})).data.data,
- deleteDate: async (d) => (await apiClient.delete(`/core-attendance/meetings/${d}`)).data.data,
-
- getTeams: async () => (await apiClient.get('/core-attendance/meetings/teams')).data.data,
- getMembers: async (d) => (await apiClient.get(`/core-attendance/meetings/${d}/members`)).data.data,
-
- saveAttendance: async (d, userIds, present) => (await apiClient.put(`/core-attendance/meetings/${d}/attendance`, {
- userIds, present
- })).data.data,
-
- summary: async (d) => (await apiClient.get(`/core-attendance/meetings/${d}/summary`)).data.data,
-
- downloadSummaryCsvForDateAndSave: async (d) => {
- const res = await apiClient.get(`/core-attendance/meetings/${d}/summary.csv`, { responseType: 'blob' });
- saveResponseAsFile(res, `attendance-${d}.csv`);
- },
-
- downloadSummaryCsvAllAndSave: async () => {
- const res = await apiClient.get('/core-attendance/meetings/summary.csv', { responseType: 'blob' });
- saveResponseAsFile(res, 'attendance-summary.csv');
- },
-
- };
-
- /** URL 동기화 */
- useEffect(() => {
- setQS({date});
- }, [date]);
-
- /** 날짜 로드 */
- useEffect(() => {
- (async () => {
- try {
- const dl = await api.getDates();
- setDates(dl.dates);
- if (!dl.dates.includes(date) && dl.dates.length > 0) setDate(dl.dates[0]);
- } catch {
- alert('날짜 목록을 불러오지 못했습니다.');
- }
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- /** 팀 로드 (리드=본인 팀만 / 오거나이저·어드민=전체) */
- useEffect(() => {
- (async () => {
- try {
- const list = await api.getTeams();
- setTeams(Array.isArray(list) ? list : []);
- // 리드(팀 1개만 내려올 때) 자동 선택
- if (!teamFilter && list?.length === 1) setTeamFilter(list[0].name);
- } catch {
- setTeams([]);
- }
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- /** 선택 날짜 → 멤버/출석 로드 */
- useEffect(() => {
- (async () => {
- if (!date) return;
- try {
- const rows = await api.getMembers(date);
- setMembers(rows);
- const init = new Set();
- rows.forEach((r) => r.present && init.add(String(r.userId)));
- setPresentSet(init);
- initialPresentSetRef.current = new Set(init); // 초기 상태 보관
- setDirty(false);
- } catch {
- setMembers([]);
- setPresentSet(new Set());
- initialPresentSetRef.current = new Set();
- setDirty(false);
- }
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [date]);
-
- /** 요약 로드 */
- useEffect(() => {
- (async () => {
- if (!date) return;
- try {
- const s = await api.summary(date);
- setSummary(s);
- } catch {
- setSummary(null);
- }
- })();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [date]);
-
- /** “전체” 옵션은 팀이 2개 이상 전달될 때(= 오거나이저/어드민)만 노출 */
- const showAllOption = teams.length > 1;
-
- /** 팀 옵션(라벨) */
- const teamOptions = useMemo(() => Array.from(new Set(teams.map((t) => t.name))).filter(Boolean), [teams]);
-
- /** 필터 적용된 멤버 */
- const filteredMembers = useMemo(() => {
- let base = members;
- if (teamFilter) base = base.filter((m) => m.team === teamFilter);
- const q = filter.trim();
- if (!q) return base;
- return base.filter((m) => m.name.includes(q));
- }, [members, filter, teamFilter]);
-
- /** 날짜 조작 */
- const addToday = async () => {
- try {
- const d = ymd();
- await api.addDate(d);
- const dl = await api.getDates();
- setDates(dl.dates);
- setDate(d);
- } catch {
- alert('날짜 추가에 실패했습니다.');
- }
- };
-
- const addSelectedDate = async () => {
- try {
- if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) {
- alert('날짜 형식이 올바르지 않습니다. YYYY-MM-DD');
- return;
- }
- await api.addDate(date);
- const dl = await api.getDates();
- setDates(dl.dates);
- alert(`"${date}"가 추가되었습니다.`);
- } catch {
- alert('선택 날짜 추가에 실패했습니다.');
- }
- };
-
- const removeDate = async (d) => {
- try {
- await api.deleteDate(d);
- const dl = await api.getDates();
- setDates(dl.dates);
- if (d === date) setDate(dl.dates[0] ?? ymd());
- } catch {
- alert('날짜 삭제에 실패했습니다.');
- }
- };
-
- /** 체크 토글(로컬 상태만 변경; 서버 전송 없음) */
- const toggleMember = (m) => {
- const id = String(m.userId);
- const next = !presentSet.has(id);
- setPresentSet((prev) => {
- const n = new Set(prev);
- next ? n.add(id) : n.delete(id);
- return n;
- });
- setDirty(true);
- };
-
- /** (필터된) 전체 체크/해제(로컬) */
- const checkAll = (value) => {
- const baseIds = filteredMembers.map((m) => String(m.userId));
- setPresentSet((prev) => {
- const n = new Set(prev);
- if (value) baseIds.forEach((id) => n.add(id)); else baseIds.forEach((id) => n.delete(id));
- return n;
- });
- setDirty(true);
- };
-
- /** 저장(Δ만 전송) */
- const saveSnapshot = async () => {
- // 현재/초기 출석 집합
- const now = presentSet;
- const init = initialPresentSetRef.current;
-
- // Δ 계산
- const added = []; // now ∖ init → present=true
- const removed = []; // init ∖ now → present=false
-
- const allIdsStr = members.map((m) => String(m.userId));
- // members에 있는 대상들만 비교 (안전)
- for (const id of allIdsStr) {
- const inNow = now.has(id);
- const inInit = init.has(id);
- if (inNow && !inInit) added.push(Number(id));
- if (!inNow && inInit) removed.push(Number(id));
- }
-
- if (!added.length && !removed.length) {
- setDirty(false);
- alert('변경된 내용이 없습니다.');
- return;
- }
-
- try {
- if (added.length) await api.saveAttendance(date, added, true);
- if (removed.length) await api.saveAttendance(date, removed, false);
- // 저장 성공 → 초기 상태 갱신
- initialPresentSetRef.current = new Set(presentSet);
- setDirty(false);
- await refreshSummary();
- alert('저장되었습니다.');
- } catch {
- alert('저장 중 오류가 발생했습니다.');
- }
- };
-
- const refreshSummary = async () => {
- try {
- setSummary(await api.summary(date));
- } catch {
- setSummary(null);
- }
- };
-
- return (
-
출석 관리
-
-
- {/* 날짜 */}
-
-
-
-
날짜
-
- 오늘 추가
- 선택 날짜
- 추가
-
-
-
- setDate(e.target.value)}
- classNames={{
- input: 'bg-transparent text-black/90 dark:text-white/90',
- inputWrapper: 'shadow-xl bg-default-200/50 dark:bg-default/60 hover:bg-default-200/70 dark:hover:bg-default/70',
- }}
- />
-
-
-
- {dates.map((d) => (
- setDate(d)} className="text-white">
- {d === date ? {d} : d}
-
- removeDate(d)}>
- 삭제
-
-
))}
- {dates.length === 0 &&
등록된 날짜가 없습니다.
}
-
-
-
-
- {/* 필터 & 저장 */}
-
-
-
- 필터 / 저장
-
- 저장{dirty ? ' *' : ''}
-
-
-
- {/* 팀 선택(오거나이저/어드민만 “전체” 노출) */}
- {
- const first = String(Array.from(keys || [])[0] ?? '');
- if (showAllOption && first === '__ALL__') setTeamFilter(''); else setTeamFilter(first || '');
- }}
- variant="bordered"
- classNames={{
- base: "text-white",
- label: "text-zinc-300",
- trigger: ["bg-zinc-900", "text-white", "border", "border-zinc-700", "data-[hover=true]:bg-zinc-800", "data-[focus=true]:ring-2", "data-[focus=true]:ring-zinc-600", "aria-expanded:ring-2", "aria-expanded:ring-zinc-600"].join(" "),
- value: "text-white",
- popoverContent: ["bg-zinc-900", "border", "border-zinc-700", "backdrop-blur-xl", "shadow-xl"].join(" "),
- listbox: "text-white",
- selectorIcon: "text-zinc-400"
- }}
- // 항목 스타일(전역)
- itemClasses={{
- base: ["rounded-md", "data-[hover=true]:bg-zinc-800", "data-[focus=true]:bg-zinc-800", "data-[selectable=true]:focus:bg-zinc-800"].join(" "),
- title: "text-white",
- description: "text-zinc-400",
- selectedIcon: "text-primary"
- }}
- >
- {showAllOption && (
- 전체
- )}
- {teamOptions.map((name) => (
- {name}
- ))}
-
-
- {/* 이름 검색 */}
- setFilter('')}
- />
-
-
- checkAll(true)} color="success" variant="flat">
- (필터된) 전체 체크
-
- checkAll(false)} color="warning" variant="flat">
- (필터된) 전체 해제
-
-
-
-
-
- {/* 요약 */}
-
-
- 요약
- api.downloadSummaryCsvForDateAndSave(date)}
- isDisabled={!date}
- >
- 날짜 CSV
-
-
- api.downloadSummaryCsvAllAndSave()}
- >
- 전체 CSV
-
-
- {summary ? (
-
전체 {summary.present} / {summary.total}
-
-
- {summary.perTeam.map((ts) => (
-
- {ts.teamName}
- {ts.present} / {ts.total}
-
))}
-
-
) : (로딩...
)}
-
-
-
-
- {/* 팀원 목록 */}
-
-
-
-
-
팀원
-
- {date} · {teamFilter || (showAllOption ? '전체 팀' : (teams[0]?.name ?? ''))}
-
-
-
-
-
-
-
- {filteredMembers.map((m) => {
- const id = String(m.userId);
- const checked = presentSet.has(id);
- return (
-
- toggleMember(m)}
- classNames={{label: 'text-white'}}
- >
- {m.name}{' '}
- ({m.team})
-
-
-
);
- })}
- {filteredMembers.length === 0 && (
-
표시할 팀원이 없습니다.
)}
-
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/core-recruit/layout.js b/src/app/core-recruit/layout.js
deleted file mode 100644
index 10272c4..0000000
--- a/src/app/core-recruit/layout.js
+++ /dev/null
@@ -1,17 +0,0 @@
-import { Suspense } from "react";
-import Loader from '@/components/ui/common/Loader.jsx';
-
-export const metadata = {
- title: "Core Recruit",
- description: "GDGoC INHA Core Member Recruitment Form",
-};
-
-export default function CoreRecruitLayout({ children }) {
- return (
- }>
- {children}
-
- );
-}
-
-
diff --git a/src/app/core-recruit/page.jsx b/src/app/core-recruit/page.jsx
deleted file mode 100644
index 0bbf3dc..0000000
--- a/src/app/core-recruit/page.jsx
+++ /dev/null
@@ -1,556 +0,0 @@
-"use client";
-
-import { useState, useMemo } from "react";
-import { useRouter } from 'next/navigation';
-import { Button, Textarea, Input, Checkbox, Autocomplete, AutocompleteItem, AutocompleteSection } from '@nextui-org/react';
-import axios from 'axios';
-import { majorOptions } from '@/constant/majorOptions';
-
-const TEAM_OPTIONS = ["HR", "BD", "TECH", "PR/DESIGN"];
-
-export default function CoreRecruit() {
- const router = useRouter();
- const [form, setForm] = useState({
- name: "",
- studentId: "",
- phone: "",
- major: "",
- email: "",
- team: "",
- motivation: "",
- wish: "",
- strengths: "",
- pledge: "",
- });
-
- const [files, setFiles] = useState([]);
- const [uploadedFileUrls, setUploadedFileUrls] = useState([]);
- const [uploading, setUploading] = useState(false);
- const [submitting, setSubmitting] = useState(false);
- const [touched, setTouched] = useState(false);
- const [agreed, setAgreed] = useState(false);
-
- const handleValueChange = (key) => (value) => {
- setForm((prev) => ({ ...prev, [key]: value }));
- };
-
- const handlePreventEnter = (event) => {
- if (event.key === 'Enter') {
- event.preventDefault();
- }
- };
-
- const handleFiles = async (e) => {
- const selected = Array.from(e.target.files || []);
- const limited = selected.slice(0, 10);
- const filtered = limited.filter((f) => f.size <= 10 * 1024 * 1024);
- setFiles(filtered);
- setUploadedFileUrls([]);
- if (filtered.length === 0) return;
- try {
- setUploading(true);
- const urls = await Promise.all(
- filtered.map(async (file) => {
- try {
- const formData = new FormData();
- formData.append('file', file);
- const res = await axios.post(`${process.env.NEXT_PUBLIC_BASE_API_URL}/fileupload`, formData, {
- headers: { 'Content-Type': 'multipart/form-data' },
- });
- const data = res?.data;
- const url = data?.url || data?.data?.url || data?.data?.[0]?.url || null;
- return url;
- } catch (err) {
- console.error('파일 업로드 실패:', file?.name, err);
- return null;
- }
- })
- );
- const successUrls = urls.filter((u) => !!u);
- setUploadedFileUrls(successUrls);
- if (successUrls.length !== filtered.length) {
- alert('일부 파일 업로드에 실패했습니다. 다시 시도해주세요.');
- }
- } catch (err) {
- console.error('파일 업로드 중 오류', err);
- alert('파일 업로드 중 오류가 발생했습니다.');
- } finally {
- setUploading(false);
- }
- };
-
- const isValid = useMemo(() => {
- if (!form.name || !form.studentId || !form.phone || !form.major || !form.email) return false;
- if (!form.team) return false;
- if (!form.motivation || form.motivation.length > 500) return false;
- if (!form.wish || form.wish.length > 500) return false;
- if (!form.pledge || form.pledge.length > 100) return false;
- if (files.some((f) => f.size > 10 * 1024 * 1024) || files.length > 10) return false;
- if (files.length > 0 && uploadedFileUrls.length !== files.length) return false;
- return true;
- }, [form, files, uploadedFileUrls]);
-
- const handleSubmit = async () => {
- setTouched(true);
- if (!isValid) {
- alert("필수 항목을 확인해주세요.");
- return;
- }
- try {
- setSubmitting(true);
- const payload = {
- ...form,
- files: (files || []).map((f) => ({ name: f.name, size: f.size, type: f.type })),
- fileUrls: uploadedFileUrls,
- };
- console.log('[CoreRecruit Submit]', payload);
- await axios.post(`${process.env.NEXT_PUBLIC_BASE_API_URL}/core-recruit`, payload, {
- headers: { 'Content-Type': 'application/json' },
- });
- router.push('/core-recruit/submit');
- } catch (e) {
- alert("제출 중 오류가 발생했습니다. 다시 시도해주세요.");
- } finally {
- setSubmitting(false);
- }
- };
-
- return (
-
-
- {/* 구글 로고 - 커스텀 그라데이션 적용 */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- GDGoC INHA Core Member 지원
-
-
-
-
-
-
{
- const num = (value || '').replace(/\D/g, '').slice(0, 8);
- setForm((prev) => ({ ...prev, studentId: num }));
- }}
- variant='bordered'
- autoComplete='off'
- labelPlacement='outside'
- placeholder='8자리 학번을 입력해주세요'
- inputMode='numeric'
- disableAutoFocus
- className='!mt-[10px]'
- classNames={{
- mainWrapper: 'h-[57px] w-full',
- label: '!text-white text-xl pb-[18px] mobile:text-lg',
- inputWrapper: `h-[57px] border-[1.5px] rounded-md text-white text-xl mobile:text-lg !border-[#bbbbbb30] group-data-[focus=true]:!border-[#bbbbbb30] group-data-[hover=true]:!border-[#bbbbbb30]`,
- input: 'text-lg mobile:text-base',
- errorMessage: 'hidden',
- }}
- />
-
-
-
setForm((prev) => ({ ...prev, major: value }))}
- >
- {majorOptions.map((major) => (
-
- {major.items.map((item) => (
-
- {item.value}
-
- ))}
-
- ))}
-
-
-
-
-
-
- 팀*
-
-
- {TEAM_OPTIONS.map((label) => (
- setForm((prev) => ({ ...prev, team: label }))}
- radius='none'
- classNames={{
- wrapper: 'hidden',
- label: `text-white text-xl w-[150px] h-[57px] flex justify-center items-center rounded-md mobile:text-base mobile:w-[27vw] mobile:h-[49px] ${form.team === label ? 'bg-[#471915] border-[1.5px] border-[#ea4335]' : 'bg-[#181818]'}`,
- }}
- >
- {label}
-
- ))}
-
-
-
-
-
- GDGoC INHA Core Member에 지원한 동기 (500자 이내) *
-
-
- {touched && (!form.motivation || form.motivation.length > 500) && (
- 필수 입력이며 500자 이내로 작성해주세요.
- )}
-
-
-
-
- 코어멤버로서 맡고 싶은 업무/프로젝트/행사/비전 (500자 이내) *
-
-
- {touched && (!form.wish || form.wish.length > 500) && (
- 필수 입력이며 500자 이내로 작성해주세요.
- )}
-
-
-
-
장점/역량 (자유롭게)
-
-
- 예시: 리더십/꼼꼼함/성실함, 자격증, 스킬, 툴(피그마/노션), 수상/대외활동/인턴 등
-
-
-
-
-
자료 첨부 (최대 10개, 파일당 10MB)
-
- {files.length > 0 && (
-
선택된 파일: {files.length}개
- )}
- {touched && (files.length > 10 || files.some((f)=> f.size > 10 * 1024 * 1024)) && (
-
파일 개수/크기 제한을 확인해주세요.
- )}
-
-
-
-
- Core Member로서의 각오 (100자 이내) *
-
-
- {touched && (!form.pledge || form.pledge.length > 100) && (
- 필수 입력이며 100자 이내로 작성해주세요.
- )}
-
-
-
-
- 마지막으로 공지 및 일정을 확인해주시기 바랍니다!
-
-
-
-
모집 일정
-
✅ 서류 지원 기간 : 2025. 12. 26.(금) - 2026. 01. 09.(금) 23:59:59
-
✅ 서류 결과 발표 : ~ 2026. 01. 10.(토)
-
✅ 면접 진행 기간 : 2026. 01. 12.(월) - 2026. 01. 16.(금)
-
※ 지원자 및 면접관의 일정에 따라 마감 전 조기 면접 진행이 가능할 수 있습니다.
-
✅ 최종 결과 발표 : ~ 2026. 01. 16.(금)
-
-
-
-
면접 안내
-
• 원칙적으로 대면 면접을 진행하며, 부득이한 경우 비대면으로 조정될 수 있습니다.
-
• 면접은 인하대학교 내부 장소에서 진행됩니다.
-
-
-
-
활동 안내
-
• 운영진으로 활동 시, 매주 1회 정기 운영진 회의에 필수 참석해야 합니다. (일정은 1월 내로 공지 드립니다.)
-
-
-
-
-
- 공지사항 및 일정을 확인하였으며, 이에 동의합니다.
-
-
-
-
- window.history.back()}
- >이전
- 제출
-
-
-
- );
-}
-
-
diff --git a/src/app/core-recruit/submit/page.jsx b/src/app/core-recruit/submit/page.jsx
deleted file mode 100644
index 7c3e56d..0000000
--- a/src/app/core-recruit/submit/page.jsx
+++ /dev/null
@@ -1,46 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { useRouter } from 'next/navigation';
-import { Button } from "@nextui-org/react";
-
-export default function CoreRecruitSubmit() {
- const router = useRouter();
- const [checkMotion, setCheckMotion] = useState(false);
-
- useEffect(() => {
- const timer = setTimeout(() => setCheckMotion(true), 50);
- return () => clearTimeout(timer);
- }, []);
-
- return (
-
-
-
- ✓
-
-
-
코어 멤버 지원서 제출이 완료되었습니다.
-
-
-
아래 일정을 한번 더 확인해주세요!
-
✅ 서류 지원 기간 : 2025. 12. 26.(금) - 2026. 01. 09.(금) 23:59:59
-
✅ 서류 결과 발표 : ~ 2026. 01. 10.(토)
-
✅ 면접 진행 기간 : 2026. 01. 12.(월) - 2026. 01. 16.(금)
-
※ 지원자 및 면접관의 일정에 따라 마감 전 조기 면접 진행이 가능할 수 있습니다.
-
✅ 최종 결과 발표 : ~ 2026. 01. 16.(금)
-
-
-
- router.push('/core-recruit')}>
- 폼으로 돌아가기
-
- router.push('/')}>메인으로 이동하기
-
-
- );
-}
-
-
diff --git a/src/app/coreadmin/layout.js b/src/app/coreadmin/layout.js
deleted file mode 100644
index ad27377..0000000
--- a/src/app/coreadmin/layout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import MenuHeader from '@/components/ui/common/MenuHeader';
-import ApiCodeGuard from '@/components/auth/ApiCodeGuard.jsx';
-
-export const metadata = {
- title: 'CoreAdmin', description: 'Core member application management',
-};
-
-export default function CoreAdminLayout({children}) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/coreadmin/page.jsx b/src/app/coreadmin/page.jsx
deleted file mode 100644
index 96ca2b1..0000000
--- a/src/app/coreadmin/page.jsx
+++ /dev/null
@@ -1,194 +0,0 @@
-"use client";
-
-import React, { useCallback, useEffect, useRef } from 'react';
-import { Table, TableHeader, TableColumn, TableBody, TableRow, TableCell, Chip } from '@nextui-org/react';
-
-import UserDetailsModal from '@/components/admin/UserDetailsModal';
-import AdminTableTopContent from '@/components/admin/AdminTableTopContent';
-import AdminTableBottomContent from '@/components/admin/AdminTableBottomContent';
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-
-const columns = [
- { name: 'NAME', uid: 'name' },
- { name: 'MAJOR / ID', uid: 'major' },
- { name: 'TEAM', uid: 'team' },
- { name: 'EMAIL', uid: 'email' },
- { name: 'PHONE', uid: 'phone' },
-];
-
-export default function CoreAdminPage() {
- const { apiClient } = useAuthenticatedApi();
-
- const [page, setPage] = React.useState(1);
- const [modalOpen, setModalOpen] = React.useState(false);
- const modalClosing = useRef(false);
-
- const [selectedUser, setSelectedUser] = React.useState(null);
- const [searchValue, setSearchValue] = React.useState('');
- const [query, setQuery] = React.useState('');
- const [loading, setLoading] = React.useState(false);
- const [error, setError] = React.useState('');
- const [currentUsers, setCurrentUsers] = React.useState([]);
- const [totalUsers, setTotalUsers] = React.useState(0);
- const [totalPages, setTotalPages] = React.useState(0);
-
- const rowsPerPage = 10;
-
- const fetchApplicants = useCallback(async () => {
- setLoading(true);
- setError('');
- try {
- const params = {
- page: page - 1,
- size: rowsPerPage,
- sort: 'createdAt',
- dir: 'DESC',
- question: query || undefined,
- };
- const res = await apiClient.get('/core-recruit/applicants', { params });
- const list = Array.isArray(res?.data?.data) ? res.data.data : [];
- const total = res?.data?.meta?.totalElements ?? list.length;
- const computedTotalPages = Math.max(1, Math.ceil(total / rowsPerPage));
-
- setCurrentUsers(list);
- setTotalUsers(total);
- setTotalPages(computedTotalPages);
- } catch (err) {
- setError(String(err?.message || 'failed to load applicants'));
- setCurrentUsers([]);
- setTotalUsers(0);
- } finally {
- setLoading(false);
- }
- }, [apiClient, page, rowsPerPage, query]);
-
- const renderCell = useCallback((user, columnKey) => {
- const value = user[columnKey];
- switch (columnKey) {
- case 'name':
- return {user?.name ?? ''} ;
- case 'major':
- return (
-
-
{user?.major ?? ''}
-
{user?.studentId ?? ''}
-
- );
- case 'team': {
- const teamLabel = user?.team ?? '';
- const teamColorMap = {
- HR: '#EA4335',
- BD: '#34A853',
- TECH: '#4285F4',
- 'PR/DESIGN': '#F9AB00',
- };
- const color = teamColorMap[teamLabel] || '#9CA3AF';
- return (
-
- {teamLabel}
-
- );
- }
- case 'email':
- return {user?.email ?? ''} ;
- case 'phone':
- return {user?.phone ?? ''} ;
- case 'createdAt':
- return {user?.createdAt ? new Date(user.createdAt).toLocaleString() : ''} ;
- default:
- return value;
- }
- }, []);
-
- const handleRowClick = async (user) => {
- if (modalClosing.current) return;
- try {
- const id = user?.id;
- if (!id) throw new Error('지원자 ID 없음');
- const res = await apiClient.get(`/core-recruit/applicants/${id}`);
- const detail = res?.data?.data ?? null;
- if (!detail) throw new Error('상세 정보 없음');
- setSelectedUser(detail);
- setModalOpen(true);
- } catch (e) {
- alert('상세 정보를 불러오는 중 오류가 발생했습니다.');
- }
- };
-
- const handleSearch = () => {
- setPage(1);
- setQuery((searchValue || '').trim());
- };
-
- const handleCloseModal = () => {
- modalClosing.current = true;
- setModalOpen(false);
- setTimeout(() => {
- modalClosing.current = false;
- }, 300);
- };
-
- useEffect(() => {
- if (searchValue === '' && query !== '') {
- setPage(1);
- setQuery('');
- }
- }, [searchValue, query]);
-
- useEffect(() => {
- fetchApplicants();
- }, [fetchApplicants]);
-
- return (
-
-
- setPage(newPage)}
- />
-
- }
- topContent={
-
- }
- >
-
- {(column) => (
-
- {column.name}
-
- )}
-
-
- {(item) => (
- handleRowClick(item)}>
- {(columnKey) => (
-
- {renderCell(item, columnKey)}
-
- )}
-
- )}
-
-
-
- {/* 기존 UserDetailsModal 재사용: 핵심 질문/자유문항은 response 배열로 표시됨 */}
-
-
- );
-}
-
-
diff --git a/src/app/design-system/page.tsx b/src/app/design-system/page.tsx
new file mode 100644
index 0000000..20866c9
--- /dev/null
+++ b/src/app/design-system/page.tsx
@@ -0,0 +1,144 @@
+'use client'
+
+import { type CSSProperties } from 'react'
+
+import { DesignSystemShowcase } from '@/components/ui/design-system/DesignSystemShowcase'
+
+type GridDevice = 'desktop' | 'mobile'
+
+type GridSpec = {
+ id: string
+ label: string
+ columns: number
+}
+
+type GridGroup = {
+ id: string
+ heading: string
+ badge: string
+ device: GridDevice
+ frameWidth: number
+ gridWidth: number
+ gutter: number
+ height: number
+ specs: GridSpec[]
+}
+
+type GridCustomProperties = CSSProperties & {
+ '--grid-count'?: number
+ '--grid-content-width'?: string
+ '--grid-frame-width'?: string
+ '--grid-gutter'?: string
+ '--grid-height'?: string
+}
+
+const DESKTOP_GRID_GROUPS: GridGroup[] = [
+ {
+ id: 'desktop-long',
+ heading: '작업영역 1120px (정보값 많은 페이지에 활용)',
+ badge: 'Long',
+ device: 'desktop',
+ frameWidth: 1280,
+ gridWidth: 1120,
+ gutter: 20,
+ height: 440,
+ specs: [
+ { id: 'desktop-long-12', label: '12단', columns: 12 },
+ { id: 'desktop-long-8', label: '8단', columns: 8 },
+ { id: 'desktop-long-4', label: '4단', columns: 4 },
+ { id: 'desktop-long-2', label: '2단', columns: 2 }
+ ]
+ },
+ {
+ id: 'desktop-short',
+ heading: '작업영역 550px (정보값 적은 페이지에 활용)',
+ badge: 'Short',
+ device: 'desktop',
+ frameWidth: 1280,
+ gridWidth: 550,
+ gutter: 20,
+ height: 400,
+ specs: [
+ { id: 'desktop-short-12', label: '12단', columns: 12 },
+ { id: 'desktop-short-8', label: '8단', columns: 8 },
+ { id: 'desktop-short-4', label: '4단', columns: 4 },
+ { id: 'desktop-short-2', label: '2단', columns: 2 }
+ ]
+ }
+]
+
+const MOBILE_GRID_GROUP: GridGroup = {
+ id: 'mobile',
+ heading: '작업영역 343px',
+ badge: 'Mobile',
+ device: 'mobile',
+ frameWidth: 375,
+ gridWidth: 343,
+ gutter: 8,
+ height: 320,
+ specs: [
+ { id: 'mobile-4', label: '4단', columns: 4 },
+ { id: 'mobile-3', label: '3단', columns: 3 },
+ { id: 'mobile-2', label: '2단', columns: 2 }
+ ]
+}
+
+const formatColumnDetail = (columns: number, gridWidth: number, gutter: number) => {
+ const totalGutter = gutter * (columns - 1)
+ const width = (gridWidth - totalGutter) / columns
+ const rounded = Number(width.toFixed(2))
+ const formatted = Number.isInteger(rounded) ? rounded.toFixed(0) : rounded.toString()
+
+ return `${formatted}px · Gutter ${gutter}px`
+}
+
+const GridPreview = ({
+ columns,
+ device,
+ frameWidth,
+ gridWidth,
+ gutter,
+ height
+}: {
+ columns: number
+ device: GridDevice
+ frameWidth: number
+ gridWidth: number
+ gutter: number
+ height: number
+}) => {
+ const customStyle: GridCustomProperties = {
+ '--grid-count': columns,
+ '--grid-frame-width': `${frameWidth}px`,
+ '--grid-content-width': `${gridWidth}px`,
+ '--grid-gutter': `${gutter}px`,
+ '--grid-height': `${height}px`
+ }
+
+ return (
+
+
+ {Array.from({ length: columns }).map((_, index) => (
+
+ ))}
+
+
+ )
+}
+
+export default function DesignSystemPage() {
+ return (
+
+
+
+
Internal Only
+
GDGoC Design System
+
+ 개발 중 확인용 임시 페이지입니다. PR 시 제거 예정.
+
+
+
+
+
+ )
+}
diff --git a/src/app/error.js b/src/app/error.js
deleted file mode 100644
index f608c0b..0000000
--- a/src/app/error.js
+++ /dev/null
@@ -1,120 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { Button } from "@nextui-org/react";
-import Image from 'next/image';
-
-// components
-import Forbidden from '@/app/forbidden';
-import Unauthorized from '@/app/unauthorized';
-
-// resource
-import gdgocIcon from "@public/icons/logo.png";
-
-/**
- * @typedef {Object} ErrorProps
- * @property {Error} error - The error object
- * @property {() => void} reset - Function to reset the error state
- */
-export default function Error({ error, reset = () => {} }) {
- const [countdown, setCountdown] = useState(5);
- const errorCode = error?.statusCode || error?.status || 500;
- const errorTitle = error?.title || 'Internal Server Error';
- const errorMessage = error?.message || 'Unknown Error has occurred';
-
- useEffect(() => {
- if (process.env.NODE_ENV === 'development') {
- console.log(error || 'Unknown Error has occurred');
- }
- }, []);
-
- useEffect(() => {
- if (countdown > 0) {
- const timer = setTimeout(() => setCountdown(countdown - 1), 1000);
- return () => clearTimeout(timer);
- }
- }, [countdown]);
-
- const handleClick = () => {
- reset();
- window.location.reload();
- };
-
- // Handle specific error codes
- if (errorCode === 401) {
- return ;
- }
-
- if (errorCode === 403) {
- return ;
- }
-
- return (
-
- {/* 아이콘 배경 */}
-
-
- {/* 오류 박스 */}
-
-
- {/* 오류 아이콘 */}
-
-
- {/* 오류 내용 */}
-
- {errorCode}
-
{errorTitle}
-
-
-
-
-
페이지 로드 중 에러가 발생하였습니다.
-
Tech팀으로 연락 바랍니다!
-
-
- {/* 개발 환경에서만 표시 */}
- {process.env.NODE_ENV === 'development' && (
-
- )}
-
- {/* 버튼 */}
-
0 ? `(${countdown}초)` : ''}`}
- >
- 다시 시도하기 {countdown > 0 && `(${countdown}초)`}
-
-
- {/* 작은 로고 */}
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/error.tsx b/src/app/error.tsx
new file mode 100644
index 0000000..f1e55bb
--- /dev/null
+++ b/src/app/error.tsx
@@ -0,0 +1,152 @@
+'use client'
+
+import { useState, useEffect } from 'react'
+import { Button } from '@nextui-org/react'
+import Image from 'next/image'
+
+// components
+import Forbidden from '@/app/forbidden'
+import Unauthorized from '@/app/unauthorized'
+
+// resource
+import gdgocIcon from '@public/icons/logo.png'
+
+interface ErrorProps {
+ error: Error & {
+ statusCode?: number;
+ status?: number;
+ title?: string;
+ message?: string
+ };
+ reset: () => void;
+}
+
+export default function Error({ error, reset }: ErrorProps) {
+ const [countdown, setCountdown] = useState(5)
+ const errorCode = error?.statusCode || error?.status || 500
+ const errorTitle = error?.title || 'Internal Server Error'
+ const errorMessage = error?.message || 'Unknown Error has occurred'
+
+ useEffect(() => {
+ if (process.env.NODE_ENV === 'development') {
+ console.log(error || 'Unknown Error has occurred')
+ }
+ }, [error])
+
+ useEffect(() => {
+ if (countdown > 0) {
+ const timer = setTimeout(() => setCountdown(countdown - 1), 1000)
+ return () => clearTimeout(timer)
+ }
+ }, [countdown])
+
+ const handleClick = () => {
+ reset()
+ window.location.reload()
+ }
+
+ // Handle specific error codes
+ if (errorCode === 401) {
+ return
+ }
+
+ if (errorCode === 403) {
+ return
+ }
+
+ return (
+
+ {/* 아이콘 배경 */}
+
+
+ {/* 오류 박스 */}
+
+
+ {/* 오류 아이콘 */}
+
+
+ {/* 오류 내용 */}
+
+ {errorCode}
+
+ {errorTitle}
+
+
+
+
+
+
+ 페이지 로드 중 에러가 발생하였습니다.
+
+
Tech팀으로 연락 바랍니다!
+
+
+ {/* 개발 환경에서만 표시 */}
+ {process.env.NODE_ENV === 'development' && (
+
+ )}
+
+ {/* 버튼 */}
+
0 ? `(${countdown}초)` : ''}`}
+ >
+ 다시 시도하기 {countdown > 0 && `(${countdown}초)`}
+
+
+ {/* 작은 로고 */}
+
+
+
+
+
+
+ )
+}
diff --git a/src/app/event/homecoming/admin/guestbook/page.jsx b/src/app/event/homecoming/admin/guestbook/page.jsx
deleted file mode 100644
index bdcccc3..0000000
--- a/src/app/event/homecoming/admin/guestbook/page.jsx
+++ /dev/null
@@ -1,125 +0,0 @@
-'use client';
-
-import { useState, useRef, useCallback } from "react";
-import { useAuthenticatedApi } from "@/hooks/useAuthenticatedApi";
-import { useGuestbookEntries } from "@/hooks/homecoming/useGuestbookEntries";
-import GuestbookWordCloud from "@/components/event/homecoming/GuestbookWordCloud";
-
-export default function GuestbookAdminPage() {
- const { entries, isLoading, error, lastSyncedAt, refresh } = useGuestbookEntries();
- const { apiClient } = useAuthenticatedApi();
- const [formValues, setFormValues] = useState({ wristbandSerial: "", name: "" });
- const [isSubmitting, setIsSubmitting] = useState(false);
- const [statusMessage, setStatusMessage] = useState("");
- const statusTimerRef = useRef(null);
-
- const handleChange = (event) => {
- const { name, value } = event.target;
- setFormValues((prev) => ({ ...prev, [name]: value }));
- };
-
- const resetStatusTimer = useCallback(() => {
- if (statusTimerRef.current) {
- clearTimeout(statusTimerRef.current);
- }
- statusTimerRef.current = setTimeout(() => setStatusMessage(""), 2500);
- }, []);
-
- const handleSubmit = async (event) => {
- event.preventDefault();
- if (!formValues.wristbandSerial.trim() || !formValues.name.trim()) {
- return;
- }
-
- try {
- setIsSubmitting(true);
- await apiClient.post("/guestbook/entries", {
- wristbandSerial: formValues.wristbandSerial.trim(),
- name: formValues.name.trim(),
- });
- setFormValues({ wristbandSerial: "", name: "" });
- setStatusMessage("입장 등록이 완료되었습니다.");
- resetStatusTimer();
- await refresh();
- } catch (err) {
- console.error("방명록 등록 실패", err);
- const message = err?.response?.data?.message || "입장 등록에 실패했습니다.";
- setStatusMessage(message);
- resetStatusTimer();
- } finally {
- setIsSubmitting(false);
- }
- };
-
- const isSubmitDisabled = isSubmitting || !formValues.wristbandSerial.trim() || !formValues.name.trim();
-
- return (
-
-
-
-
-
-
-
입장 등록
-
현재 {entries.length}명의 게스트가 입장했습니다.
- {error &&
{error}
}
-
-
-
- 손목띠지 번호
-
-
-
- 이름
-
-
-
-
- {isSubmitting ? "등록 중..." : "방명록 등록"}
-
-
- {statusMessage &&
{statusMessage}
}
- {lastSyncedAt && (
-
- 마지막 동기화: {lastSyncedAt.toLocaleTimeString("ko-KR", { hour12: false })}
-
- )}
-
-
-
-
- );
-}
diff --git a/src/app/event/homecoming/admin/layout.js b/src/app/event/homecoming/admin/layout.js
deleted file mode 100644
index ae9fdb2..0000000
--- a/src/app/event/homecoming/admin/layout.js
+++ /dev/null
@@ -1,9 +0,0 @@
-import ApiCodeGuard from "@/components/auth/ApiCodeGuard";
-
-export default function HomecomingAdminLayout({children}) {
- return (
-
- {children}
-
- );
-}
diff --git a/src/app/event/homecoming/admin/luckydraw/page.jsx b/src/app/event/homecoming/admin/luckydraw/page.jsx
deleted file mode 100644
index 77bdc16..0000000
--- a/src/app/event/homecoming/admin/luckydraw/page.jsx
+++ /dev/null
@@ -1,283 +0,0 @@
-'use client';
-
-import { useAuthenticatedApi } from "@/hooks/useAuthenticatedApi";
-import { useCallback, useEffect, useMemo, useRef, useState } from "react";
-
-const clampDrawCount = (value) => {
- const parsed = parseInt(value, 10);
- const safe = Number.isNaN(parsed) ? 1 : parsed;
- return Math.min(Math.max(safe, 1), 50);
-};
-
-const formatKoreanTime = (dateStr) => {
- if (!dateStr) {
- return "-";
- }
- try {
- return new Intl.DateTimeFormat('ko-KR', {
- hour12: false,
- month: '2-digit',
- day: '2-digit',
- hour: '2-digit',
- minute: '2-digit',
- }).format(new Date(dateStr));
- } catch (err) {
- return dateStr;
- }
-};
-
-export default function LuckyDrawAdminPage() {
- const { apiClient } = useAuthenticatedApi();
- const [entriesCount, setEntriesCount] = useState(0);
- const [winners, setWinners] = useState([]);
- const [drawCount, setDrawCount] = useState("1");
- const [isLoading, setIsLoading] = useState(true);
- const [isDrawing, setIsDrawing] = useState(false);
- const [isResetting, setIsResetting] = useState(false);
- const [statusMessage, setStatusMessage] = useState("");
- const [error, setError] = useState("");
- const [recentWinnerIds, setRecentWinnerIds] = useState([]);
- const statusTimerRef = useRef(null);
- const highlightTimerRef = useRef(null);
-
- const fetchEntriesCount = useCallback(async () => {
- try {
- const res = await apiClient.get("/guestbook/entries");
- const list = res?.data?.data ?? [];
- setEntriesCount(Array.isArray(list) ? list.length : 0);
- } catch (err) {
- console.error("참가자 목록 조회 실패", err);
- throw err;
- }
- }, [apiClient]);
-
- const fetchWinners = useCallback(async () => {
- try {
- const res = await apiClient.get("/guestbook/lucky-draw/winners");
- const list = res?.data?.data ?? [];
- setWinners(Array.isArray(list) ? list : []);
- } catch (err) {
- console.error("당첨자 목록 조회 실패", err);
- throw err;
- }
- }, [apiClient]);
-
- const refreshAll = useCallback(async () => {
- setIsLoading(true);
- setError("");
- try {
- await Promise.all([fetchEntriesCount(), fetchWinners()]);
- } catch (err) {
- setError('데이터를 불러오지 못했습니다. 잠시 후 다시 시도해주세요.');
- } finally {
- setIsLoading(false);
- }
- }, [fetchEntriesCount, fetchWinners]);
-
- useEffect(() => {
- refreshAll();
- }, [refreshAll]);
-
- useEffect(() => () => {
- if (statusTimerRef.current) {
- clearTimeout(statusTimerRef.current);
- }
- if (highlightTimerRef.current) {
- clearTimeout(highlightTimerRef.current);
- }
- }, []);
-
- const scheduleStatusClear = useCallback(() => {
- if (statusTimerRef.current) {
- clearTimeout(statusTimerRef.current);
- }
- statusTimerRef.current = setTimeout(() => setStatusMessage(""), 2500);
- }, []);
-
- const highlightRecent = useCallback((ids) => {
- if (highlightTimerRef.current) {
- clearTimeout(highlightTimerRef.current);
- }
- if (!ids.length) {
- setRecentWinnerIds([]);
- return;
- }
- setRecentWinnerIds(ids);
- highlightTimerRef.current = setTimeout(() => setRecentWinnerIds([]), 4000);
- }, []);
-
- const handleDraw = async (event) => {
- event.preventDefault();
- const sanitized = clampDrawCount(drawCount);
- setDrawCount(String(sanitized));
- setIsDrawing(true);
- setError("");
- try {
- const res = await apiClient.post("/guestbook/lucky-draw", { count: sanitized });
- const newWinners = res?.data?.data ?? [];
- highlightRecent(newWinners.map((winner) => winner.id));
- setStatusMessage(`${sanitized}명의 당첨자를 추첨했습니다.`);
- scheduleStatusClear();
- await Promise.all([fetchWinners(), fetchEntriesCount()]);
- } catch (err) {
- console.error("럭키드로우 추첨 실패", err);
- const message = err?.response?.data?.message || '추첨에 실패했습니다.';
- setError(message);
- } finally {
- setIsDrawing(false);
- }
- };
-
- const handleReset = async () => {
- if (typeof window !== 'undefined' && !window.confirm('정말로 당첨자 목록을 초기화할까요?')) {
- return;
- }
- setIsResetting(true);
- setError("");
- try {
- await apiClient.post("/guestbook/lucky-draw/reset");
- setStatusMessage('당첨자 목록을 초기화했습니다.');
- highlightRecent([]);
- scheduleStatusClear();
- await fetchWinners();
- } catch (err) {
- console.error("럭키드로우 리셋 실패", err);
- const message = err?.response?.data?.message || '초기화에 실패했습니다.';
- setError(message);
- } finally {
- setIsResetting(false);
- }
- };
-
- const sortedWinners = useMemo(() => {
- const getTimeValue = (value) => {
- const timestamp = value ? new Date(value).getTime() : 0;
- return Number.isNaN(timestamp) ? 0 : timestamp;
- };
- return [...winners].sort((a, b) => getTimeValue(b.wonAt) - getTimeValue(a.wonAt));
- }, [winners]);
-
- const recentCount = sortedWinners.length;
-
- return (
-
-
-
-
-
-
-
-
추첨하기
- 1회 최대 50명
-
-
-
- 추첨 인원 수
- setDrawCount(event.target.value)}
- className="w-full rounded-2xl border border-slate-200 bg-slate-50 px-5 py-3 text-base text-slate-900 placeholder-slate-400 focus:outline-none focus:border-cblue focus:bg-white transition-all"
- />
-
-
-
- {isDrawing ? '추첨 중...' : '추첨하기'}
-
-
- {isResetting ? '초기화 중...' : '당첨자 초기화'}
-
-
- {statusMessage}
- {error && {error}
}
-
-
-
-
-
현재 상태
-
-
-
총 참가자
-
{entriesCount.toLocaleString()}명
-
-
-
당첨자 수
-
{recentCount.toLocaleString()}명
-
-
- 추첨 후에는 자동으로 명단이 업데이트됩니다. 운영 중 중복 추첨을 피하려면 새로고침으로 상태를 확인하세요.
-
-
-
-
-
-
-
-
-
당첨자 명단
-
당첨 시간 순으로 정렬됩니다.
-
- {!sortedWinners.length && !isLoading && (
-
아직 당첨자가 없습니다.
- )}
-
- {isLoading ? (
- 불러오는 중...
- ) : (
-
- {sortedWinners.map((winner) => (
-
-
{winner.name}
-
{winner.wristbandSerial}
-
{formatKoreanTime(winner.wonAt)}
-
- ))}
-
- )}
-
-
-
- );
-}
diff --git a/src/app/event/homecoming/admin/page.jsx b/src/app/event/homecoming/admin/page.jsx
deleted file mode 100644
index a892573..0000000
--- a/src/app/event/homecoming/admin/page.jsx
+++ /dev/null
@@ -1,44 +0,0 @@
-import Link from "next/link";
-
-export default function HomecomingAdminPage() {
- const sections = [
- {
- title: "방명록",
- description: "입장 등록 및 워드클라우드 화면",
- href: "/event/homecoming/admin/guestbook",
- },
- {
- title: "럭키드로우",
- description: "당첨자 추첨 및 초기화",
- href: "/event/homecoming/admin/luckydraw",
- },
- ];
-
- return (
-
-
-
-
-
- {sections.map((section) => (
-
-
{section.title}
-
{section.description}
-
- 바로가기 →
-
-
- ))}
-
-
-
- );
-}
diff --git a/src/app/event/homecoming/component/mobile/HomecomingInviteCard.jsx b/src/app/event/homecoming/component/mobile/HomecomingInviteCard.jsx
deleted file mode 100644
index cfad189..0000000
--- a/src/app/event/homecoming/component/mobile/HomecomingInviteCard.jsx
+++ /dev/null
@@ -1,221 +0,0 @@
-'use client';
-
-import React, {useMemo} from 'react';
-import {GoogleMap, Marker, useJsApiLoader} from '@react-google-maps/api';
-import {useSearchParams} from "next/navigation";
-import decodeHashToName from "@/app/event/homecoming/util/decoder";
-
-export default function HomecomingInviteCard() {
- const sp = useSearchParams();
- const hash = sp.get('hash');
- const userName = useMemo(() => decodeHashToName(hash)?.trim() ?? '', [hash]);
-
- return (
-
- {/* 상단 바 */}
-
-
- {/* 상단 컬러 라인 */}
-
-
-
- {/* GDGoC 로고 */}
-
-
- G
- D
- G
- o
- C
- INHA
-
-
-
- {/* 초대 문구 */}
-
-
- 제 1회 홈커밍 데이 에
-
- {userName ? (<>
- {userName} 님을 초대합니다!
- >) : (<>여러분을 초대합니다!>)}
-
-
-
- {/* 내용 블록 */}
-
{/* 일시 */}
-
-
일시
-
-
- 2025년 12월 20일 (토) 13:00 ~ 19:00
-
-
- * 19:00 이후 뒤풀이 장소로 함께 이동하며 마무리합니다.
-
-
-
-
- {/* 프로그램 */}
-
-
프로그램
-
-
- {/* 요약 */}
-
- 오후 1시 입장을 시작으로, 1부 프로젝트 성과 발표회와 시상,
-
- 이후 오프닝 특강과 연간 활동 소개, OB 및 초청자 인사를 거쳐
-
- 팀별 경쟁 게임·퀴즈·자유 네트워킹으로 이어지는 구성입니다.
-
- 행사는 13:00–19:00까지 진행되며,
-
- 마지막에는 전체 교류 마무리 후 뒤풀이 이동으로 마무리됩니다.
-
-
- {/* 1부 */}
-
-
1부 (13:00–15:30) · GOAT 프로젝트 데모데이
-
-
-
- 13:00–13:20
- 입장/체크인 · 오프닝 안내 · 활동 소개 영상
-
-
- 13:20–13:30
- 라운드 운영 안내 · 발표 준비
-
-
- 13:30–15:10
- 프로젝트 성과 발표/데모 (총 6라운드) · QnA
-
-
- 15:10–15:30
- 심사 집계 · 시상식
-
-
-
-
- {/* 2부 */}
-
-
2부 (15:30–19:00) · IN·Fest
-
-
-
- 15:30–16:00
- 2부 입장
-
-
- 16:00–16:40
-
- 오프닝 특강
-
- GDG Campus Korea 김대현님
-
-
-
- 16:40–17:00
- GDGoC INHA 연간 활동 소개
-
-
- 17:00–17:30
- OB 및 초청자 인사
-
-
- 17:30–18:30
- 네트워킹 게임 · 퀴즈 프로그램
-
-
- 18:30–19:00
- 자유 네트워킹
-
-
- 19:00–
- 뒤풀이 진행
-
-
-
-
- {/* 도착 안내 */}
-
-
도착 안내
-
- • 1부 참석자: 12:50 까지 도착
- • 2부 참석자: 15:50 까지 도착
-
-
-
- {/* 문의 */}
-
-
문의
-
- 행사 관련 문의: 010-2087-1816
-
-
-
-
-
- {/* 장소 */}
-
-
장소
-
-
신한 스퀘어 브릿지 인천
-
(인천광역시 연수구 컨벤시아대로 204, 인스타2)
-
-
-
- {/* 지도 */}
-
-
-
-
- {/* 하단 장식 */}
-
-
-
);
-}
-
-
-function HomecomingMap() {
- const {isLoaded, loadError} = useJsApiLoader({
- googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY, id: 'homecoming-map-script',
- });
-
- const center = {lat: 37.388493, lng: 126.639989};
-
- if (loadError) {
- return (
- 지도를 불러오는 중 오류가 발생했습니다.
-
);
- }
-
- if (!isLoaded) {
- return (
- 지도를 불러오는 중입니다...
-
);
- }
-
- return (
-
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/mobile/HomecomingMobile.jsx b/src/app/event/homecoming/component/mobile/HomecomingMobile.jsx
deleted file mode 100644
index 349e234..0000000
--- a/src/app/event/homecoming/component/mobile/HomecomingMobile.jsx
+++ /dev/null
@@ -1,63 +0,0 @@
-'use client';
-
-import {useEffect, useRef, useState} from 'react';
-import HomecomingInviteCard from './HomecomingInviteCard';
-
-export default function HomecomingMobile() {
- const MIN_TOP = 64;
- const IMAGE_HEIGHT = 278;
- const EXTRA_GAP = 24;
-
- const MAX_TOP = MIN_TOP + IMAGE_HEIGHT + EXTRA_GAP;
-
- const [top, setTop] = useState(MAX_TOP);
- const scrollRef = useRef(null);
-
- useEffect(() => {
- const el = scrollRef.current;
- if (!el) return;
-
- const RANGE = IMAGE_HEIGHT + EXTRA_GAP; // 302
-
- const handleScroll = () => {
- const scrollY = el.scrollTop;
-
- const progress = Math.min(scrollY / RANGE, 1);
- const nextTop = MAX_TOP - RANGE * progress;
-
- setTop(nextTop);
- };
-
- handleScroll();
- el.addEventListener('scroll', handleScroll, {passive: true});
- return () => el.removeEventListener('scroll', handleScroll);
- }, []);
-
- return (
- {/* 상단 고정 영역 */}
-
-
-
-
-
-
-
-
-
-
- {/* 카드 */}
-
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/pc/Frame.jsx b/src/app/event/homecoming/component/pc/Frame.jsx
deleted file mode 100644
index 214b16f..0000000
--- a/src/app/event/homecoming/component/pc/Frame.jsx
+++ /dev/null
@@ -1,55 +0,0 @@
-'use client';
-
-export default function Frame() {
- return (
- {/* =====================
- 상단 장식 라인
- ===================== */}
-
- {/* 왼쪽 상단 라인 */}
-
-
- {/* 오른쪽 상단 라인 */}
-
-
-
- {/* =====================
- 하단 장식 라인
- ===================== */}
-
- {/* 오른쪽 하단 라인 */}
-
-
- {/* 왼쪽 하단 라인 */}
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/pc/FrameLayout.jsx b/src/app/event/homecoming/component/pc/FrameLayout.jsx
deleted file mode 100644
index 1b9e7a5..0000000
--- a/src/app/event/homecoming/component/pc/FrameLayout.jsx
+++ /dev/null
@@ -1,54 +0,0 @@
-'use client';
-
-import {useRef, useState} from 'react';
-import Frame from './Frame';
-import FrameViewport from './FrameViewport';
-import ScrollDots from './ScrollDots';
-
-export default function FrameLayout() {
- const viewportRef = useRef(null);
- const [activeIndex, setActiveIndex] = useState(0);
- const TOTAL = 5;
-
- const onScroll = () => {
- const el = viewportRef.current;
- if (!el) return;
- const idx = Math.round(el.scrollTop / el.clientHeight);
- setActiveIndex(idx);
- };
-
- const onJump = (index) => {
- const el = viewportRef.current;
- if (!el) return;
- el.scrollTo({top: index * el.clientHeight, behavior: 'auto'});
- };
-
- const onWheel = (e) => {
- const el = viewportRef.current;
- if (!el) return;
-
- const atTop = el.scrollTop <= 0;
- const atBottom = el.scrollTop + el.clientHeight >= el.scrollHeight - 1;
- const canScrollInside = (e.deltaY > 0 && !atBottom) || (e.deltaY < 0 && !atTop);
-
- if (!canScrollInside) return;
-
- e.preventDefault();
- el.scrollTop += e.deltaY;
- };
-
- return ();
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/pc/FrameSection.jsx b/src/app/event/homecoming/component/pc/FrameSection.jsx
deleted file mode 100644
index f680a86..0000000
--- a/src/app/event/homecoming/component/pc/FrameSection.jsx
+++ /dev/null
@@ -1,9 +0,0 @@
-'use client';
-
-export default function FrameSection({ children }) {
- return (
-
- );
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/pc/FrameViewport.jsx b/src/app/event/homecoming/component/pc/FrameViewport.jsx
deleted file mode 100644
index e3049d6..0000000
--- a/src/app/event/homecoming/component/pc/FrameViewport.jsx
+++ /dev/null
@@ -1,193 +0,0 @@
-'use client';
-
-import React, {forwardRef} from 'react';
-import FrameSection from './FrameSection';
-import {GoogleMap, Marker, useJsApiLoader} from "@react-google-maps/api";
-
-const FrameViewport = forwardRef(function FrameViewport({onScroll}, ref) {
- return (
-
-
-
-
-
-
);
-});
-
-export default FrameViewport;
-
-function FirstSection() {
- return (
-
- G
- D
- G
- o
- C
- INHA
-
-
-
제 1회 홈커밍 데이
-
-
- 오후 1시 입장을 시작으로, 1부 프로젝트 성과 발표회 와 시상 ,
-
- 이후 오프닝 특강 과 연간 활동 소개 , OB 및 초청자 인사 를 거쳐
-
- 팀별 경쟁 게임·퀴즈·자유 네트워킹 으로 이어지는 구성입니다.
-
- 행사는 13:00–19:00 까지 진행되며,
-
- 마지막에는 전체 교류 마무리 후 뒤풀이 이동 으로 마무리됩니다.
-
-
);
-}
-
-/* 2) 3컬럼 타임테이블(전체 요약) */
-function SecondSection() {
- return (
-
타임테이블
-
- {/* Col 1 */}
-
-
-
-
-
-
-
-
- {/* Col 3 */}
-
-
-
- GDG Campus Korea 김대현님
- >}
- />
-
-
-
-
-
-
-
-
-
- • 1부 참석자: 12:50 까지 도착
- • 2부 참석자: 15:50 까지 도착
-
-
);
-}
-
-/* 3) 1부 상세 (2컬럼 리스트) */
-function ThirdSection() {
- return (
-
1부 · GOAT 프로젝트 데모데이
-
13:00–15:30
-
-
-
);
-}
-
-/* 4) 2부 카드형 */
-function FourthSection() {
- return (
-
2부 · IN·Fest
-
15:30–19:00
-
-
-
);
-}
-
-/* 5) 장소 + 지도 */
-function FifthSection() {
- return (
-
신한 스퀘어 브릿지 인천
-
- (인천광역시 연수구 컨벤시아대로 204 인스타2)
-
-
-
-
-
- 문의사항이 있으실 경우, 010-2087-1816 으로 편하게 연락 부탁드립니다.
-
-
);
-}
-
-function TimeRow({time, title, desc}) {
- return (
-
{time}
-
-
{title}
- {desc ?
{desc}
: null}
-
-
);
-}
-
-
-function HomecomingMap() {
- const {isLoaded, loadError} = useJsApiLoader({
- googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY, id: 'homecoming-map-script',
- });
-
- const center = {lat: 37.388493, lng: 126.639989};
-
- if (loadError) {
- return (
- 지도를 불러오는 중 오류가 발생했습니다.
-
);
- }
-
- if (!isLoaded) {
- return (
- 지도를 불러오는 중입니다...
-
);
- }
-
- return (
-
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/pc/HeroIntro.jsx b/src/app/event/homecoming/component/pc/HeroIntro.jsx
deleted file mode 100644
index 856c2ea..0000000
--- a/src/app/event/homecoming/component/pc/HeroIntro.jsx
+++ /dev/null
@@ -1,68 +0,0 @@
-'use client';
-
-export default function HeroIntro({userName, phase, onEnter, leaving}) {
- return (
- {/* 배경 */}
-
-
- {/* 콘텐츠 */}
-
-
- {/* 로고 */}
-
- G
- D
- G
- o
- C
- INHA
-
-
- {/* 문구 */}
-
- 제 1회 홈커밍 데이 에{' '}
- {userName ? (<>
- {userName} 님을 초대합니다!
- >) : (<>여러분을 초대합니다!>)}
-
-
- {/* CTA 버튼 */}
-
- 초대장 펼쳐보기
-
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/pc/HomecomingDesktop.jsx b/src/app/event/homecoming/component/pc/HomecomingDesktop.jsx
deleted file mode 100644
index 2a96dc5..0000000
--- a/src/app/event/homecoming/component/pc/HomecomingDesktop.jsx
+++ /dev/null
@@ -1,61 +0,0 @@
-'use client';
-
-import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
-import {useSearchParams} from 'next/navigation';
-
-import HeroIntro from './HeroIntro';
-import FrameLayout from './FrameLayout';
-import decodeHashToName from '../../util/decoder';
-
-export default function HomecomingDesktop() {
- const sp = useSearchParams();
- const hash = sp.get('hash');
- const userName = useMemo(() => decodeHashToName(hash)?.trim() ?? '', [hash]);
-
- const [heroPhase, setHeroPhase] = useState(0);
- const [mode, setMode] = useState('hero'); // 'hero' | 'frame'
- const lockRef = useRef(false);
-
- useEffect(() => {
- const t = setTimeout(() => setHeroPhase(1), 500);
- return () => clearTimeout(t);
- }, []);
-
- const enterFrame = useCallback(() => {
- if (lockRef.current || mode === 'frame') return;
- lockRef.current = true;
-
- setTimeout(() => {
- setMode('frame');
- lockRef.current = false;
- }, 700);
- }, [mode]);
-
- return (
-
-
-
-
- {/* Hero */}
-
-
-
-
- {/* Frame */}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/component/pc/ScrollDots.jsx b/src/app/event/homecoming/component/pc/ScrollDots.jsx
deleted file mode 100644
index e0d2ddf..0000000
--- a/src/app/event/homecoming/component/pc/ScrollDots.jsx
+++ /dev/null
@@ -1,15 +0,0 @@
-'use client'
-
-export default function ScrollDots({count, activeIndex, onJump}) {
- return (
- {Array.from({length: count}).map((_, i) => ( onJump?.(i)}
- className={`
- w-3 h-3 rounded-full transition-all duration-300
- ${i === activeIndex ? 'bg-white scale-125' : 'bg-white/30 hover:bg-white/60'}
- `}
- aria-label={`Go to section ${i + 1}`}
- />))}
-
);
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/guestbook/layout.jsx b/src/app/event/homecoming/guestbook/layout.jsx
deleted file mode 100644
index 57df161..0000000
--- a/src/app/event/homecoming/guestbook/layout.jsx
+++ /dev/null
@@ -1,8 +0,0 @@
-export const metadata = {
- title: "Homecoming Guestbook Cloud",
- description: "현재 입장한 게스트 이름을 실시간으로 보여줍니다.",
-};
-
-export default function HomecomingGuestbookLayout({ children }) {
- return children;
-}
diff --git a/src/app/event/homecoming/guestbook/page.jsx b/src/app/event/homecoming/guestbook/page.jsx
deleted file mode 100644
index e44a85a..0000000
--- a/src/app/event/homecoming/guestbook/page.jsx
+++ /dev/null
@@ -1,34 +0,0 @@
-'use client';
-
-import { useGuestbookEntries } from "@/hooks/homecoming/useGuestbookEntries";
-import GuestbookWordCloud from "@/components/event/homecoming/GuestbookWordCloud";
-
-export default function GuestbookWordCloudPage() {
- const { entries, isLoading, error, lastSyncedAt } = useGuestbookEntries();
-
- return (
-
-
-
-
-
현재 입장 {entries.length}명
- {lastSyncedAt && (
-
- 업데이트 {lastSyncedAt.toLocaleTimeString('ko-KR', { hour12: false })}
-
- )}
- {error &&
{error}
}
-
-
-
-
- );
-}
diff --git a/src/app/event/homecoming/hooks/useDeviceType.js b/src/app/event/homecoming/hooks/useDeviceType.js
deleted file mode 100644
index abc94c6..0000000
--- a/src/app/event/homecoming/hooks/useDeviceType.js
+++ /dev/null
@@ -1,25 +0,0 @@
-'use client';
-
-import { useEffect, useState } from 'react';
-
-export function useDeviceType() {
- const [device, setDevice] = useState(null);
-
- useEffect(() => {
- const check = () => {
- if (typeof window === 'undefined') return;
-
- const width = window.innerWidth;
- setDevice(width <= 768 ? 'mobile' : 'desktop');
- };
-
- check();
- window.addEventListener('resize', check);
-
- return () => {
- window.removeEventListener('resize', check);
- };
- }, []);
-
- return device;
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/layout.js b/src/app/event/homecoming/layout.js
deleted file mode 100644
index 0c72678..0000000
--- a/src/app/event/homecoming/layout.js
+++ /dev/null
@@ -1,34 +0,0 @@
-const siteUrl = new URL("https://gdgocinha.com");
-
-export const metadata = {
- metadataBase: siteUrl,
-
- title: "Homecoming", description: "GDGoC INHA 제1회 홈커밍 데이 행사 안내 및 참여 페이지",
-
- alternates: {
- canonical: "/homecoming",
- },
-
- openGraph: {
- title: "GDGoC INHA 제1회 홈커밍 데이",
- description: "GDGoC INHA가 처음으로 선보이는 홈커밍 데이에 여러분을 초대합니다.",
- url: "/event/homecoming",
- siteName: "GDGoC INHA",
- images: [{
- url: "/images/homecoming/meta_img.png", width: 1143, height: 750, alt: "GDGoC INHA Homecoming Day",
- },],
- locale: "ko_KR",
- type: "website",
- },
-
- twitter: {
- card: "summary_large_image",
- title: "GDGoC INHA 제1회 홈커밍 데이",
- description: "GDGoC INHA가 처음으로 선보이는 홈커밍 데이에 여러분을 초대합니다.",
- images: ["/images/homecoming/meta_img.png"],
- },
-};
-
-export default function HomecomingLayout({ children }) {
- return children;
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/page.jsx b/src/app/event/homecoming/page.jsx
deleted file mode 100644
index 0165b78..0000000
--- a/src/app/event/homecoming/page.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-'use client';
-
-import { useDeviceType } from './hooks/useDeviceType';
-import HomecomingMobile from './component/mobile/HomecomingMobile';
-import HomecomingDesktop from './component/pc/HomecomingDesktop';
-
-export default function Page() {
- const device = useDeviceType();
-
- if (device === null) {
- return
;
- }
-
- return device === 'mobile'
- ?
- : ;
-}
\ No newline at end of file
diff --git a/src/app/event/homecoming/util/decoder.js b/src/app/event/homecoming/util/decoder.js
deleted file mode 100644
index 2eddccb..0000000
--- a/src/app/event/homecoming/util/decoder.js
+++ /dev/null
@@ -1,20 +0,0 @@
-export default function decodeHashToName(hash) {
- if (!hash) return '';
- try {
- // base64url -> base64
- let b64 = hash.replace(/-/g, '+').replace(/_/g, '/');
- while (b64.length % 4 !== 0) b64 += '=';
-
- // base64 -> UTF-8 bytes -> string
- const bin = atob(b64);
- const bytes = Uint8Array.from(bin, (c) => c.charCodeAt(0));
- const shifted = new TextDecoder('utf-8').decode(bytes);
-
- // 문자 단위로 -3 시프트
- return [...shifted]
- .map((c) => String.fromCharCode(c.charCodeAt(0) - 3))
- .join('');
- } catch {
- return '';
- }
-}
\ No newline at end of file
diff --git a/src/app/forbidden.js b/src/app/forbidden.tsx
similarity index 100%
rename from src/app/forbidden.js
rename to src/app/forbidden.tsx
diff --git a/src/app/homecoming/component/HomecomingPage.jsx b/src/app/homecoming/component/HomecomingPage.jsx
deleted file mode 100644
index 4b7c7ef..0000000
--- a/src/app/homecoming/component/HomecomingPage.jsx
+++ /dev/null
@@ -1,274 +0,0 @@
-'use client';
-
-import {useState} from 'react';
-import {GoogleMap, Marker, useJsApiLoader} from '@react-google-maps/api';
-import Image from 'next/image';
-import {useSearchParams} from "next/navigation";
-
-export default function HomecomingPage() {
- const [showInvitation, setShowInvitation] = useState(false);
-
- return (
- {/* 🔥 모바일/태블릿: 화면에 딱 고정되는 배경 (fixed) */}
-
-
- {/* 💻 PC: 내용 높이만큼 아래로 늘어나는 배경 */}
-
-
- {/* 1920px 기준 캔버스 */}
-
-
- {/* 실제 콘텐츠 */}
-
- {showInvitation ? ( setShowInvitation(false)}/>) : (
- setShowInvitation(true)}/>)}
-
-
- );
-}
-
-/* ===============================
- Hero 컴포넌트
-================================ */
-function Hero({onOpenInvitation}) {
- const searchParams = useSearchParams();
-
- // 예: /homecoming?from=GOAT+멤버
- const rawText = searchParams.get('from'); // 없으면 null
- const testText = rawText ? (decodeURIComponent(rawText) + " ") : ''; // 기본값 처리
-
- return (
-
-
-
- GDGoC INHA{" "}
-
- 제1회 홈커밍데이에
-
- {testText && (<>{testText}>)}
- 여러분을 초대합니다!
-
-
-
- 초대장 펼쳐보기
-
- );
-}
-
-/* ===============================
- Invitation 컴포넌트
-================================ */
-function Invitation({onBack}) {
- return (
- {/* 초대장 카드 */}
-
-
- {/* 상단 라벨 + 뒤로가기 */}
-
-
- Invitation
-
- {onBack && (
- ← 메인으로
- )}
-
-
- {/* 1. 소개 */}
-
-
- GDGoC INHA{" "}
-
- 제1회 홈커밍 데이
-
-
- GDGoC INHA Homecoming Day 2025
-
-
-
-
-
- GDGoC INHA가 처음으로 선보이는{" "}
- 제1회 홈커밍 데이(Homecoming Day) 가 열립니다!
-
-
-
- 이번 행사는 현역 부원과 OB, 그리고 GDG 커뮤니티에 관심 있는 모든 분들이 한자리에 모여{" "}
-
- 프로젝트 성과 공유 · 기술 교류 · 커뮤니티 네트워킹
-
- 을 함께 나누는 뜻깊은 자리입니다.
-
-
-
-
- 한 해 동안의 활동을 돌아보고, 앞으로 GDGoC INHA가 만들어갈 방향을 함께 이야기하며{" "}
- 커뮤니티의 가치를 더욱 확장하는 의미 있는 시간을 준비했습니다.
-
-
-
-
- {/* 2~3. 일시/장소 + 프로그램 (PC에서 2열 배치) */}
-
- {/* 왼쪽 컬럼: 일시 + 장소 */}
-
- {/* 일시 */}
-
-
- 일시
-
-
- 2025년 12월 20일 (토)
-
-
-
- {/* 장소 */}
-
-
- 장소
-
-
- 신한 스퀘어 브릿지 인천
-
- (인천 연수구 컨벤시아대로 204 인스타2)
-
-
-
-
-
-
- {/* 오른쪽 컬럼: 프로그램 안내 */}
-
-
- 프로그램 안내
-
-
-
-
- 1부(13:00~) GOAT Final Day
-
-
-
- 팀 프로젝트 데모 시연 및 피칭 발표 (라운드 형식)
-
-
-
-
-
-
- 2부(16:00~) Networking Session
-
-
- 오프닝 특강 - GDG Campus Korea Organizer 김대현님
- GDGoC INHA 연간 활동 소개 및 커뮤니티 정리
- OB 및 초청 연사 소개
- 팀 기반 네트워킹 게임 진행
- 자유로운 네트워킹
-
-
-
-
-
-
-
-
- {/* 4. 지도 (찾아오는 길) */}
-
-
-
);
-}
-
-function HomecomingMap() {
- const {isLoaded, loadError} = useJsApiLoader({
- googleMapsApiKey: process.env.NEXT_PUBLIC_GOOGLE_MAPS_API_KEY, id: 'homecoming-map-script',
- });
-
- const center = {lat: 37.388493, lng: 126.639989};
-
- if (loadError) {
- return (
- 지도를 불러오는 중 오류가 발생했습니다.
-
);
- }
-
- if (!isLoaded) {
- return (
- 지도를 불러오는 중입니다...
-
);
- }
-
- return (
-
-
-
-
);
-}
diff --git a/src/app/homecoming/layout.js b/src/app/homecoming/layout.js
deleted file mode 100644
index 4194324..0000000
--- a/src/app/homecoming/layout.js
+++ /dev/null
@@ -1,37 +0,0 @@
-import {Suspense} from "react";
-import Loader from "@/components/ui/common/Loader.jsx";
-
-const siteUrl = new URL("https://gdgocinha.com");
-
-export const metadata = {
- metadataBase: siteUrl,
-
- title: "Homecoming", description: "GDGoC INHA 제1회 홈커밍 데이 행사 안내 및 참여 페이지",
-
- alternates: {
- canonical: "/homecoming",
- },
-
- openGraph: {
- title: "GDGoC INHA 제1회 홈커밍 데이",
- description: "GDGoC INHA가 처음으로 선보이는 홈커밍 데이에 여러분을 초대합니다.",
- url: "/homecoming",
- siteName: "GDGoC INHA",
- images: [{
- url: "/images/homecoming/meta_img.png", width: 1143, height: 750, alt: "GDGoC INHA Homecoming Day",
- },],
- locale: "ko_KR",
- type: "website",
- },
-
- twitter: {
- card: "summary_large_image",
- title: "GDGoC INHA 제1회 홈커밍 데이",
- description: "GDGoC INHA가 처음으로 선보이는 홈커밍 데이에 여러분을 초대합니다.",
- images: ["/images/homecoming/meta_img.png"],
- },
-};
-
-export default function HomecomingLayout({ children }) {
- return children;
-}
\ No newline at end of file
diff --git a/src/app/homecoming/page.jsx b/src/app/homecoming/page.jsx
deleted file mode 100644
index b6141b3..0000000
--- a/src/app/homecoming/page.jsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import HomecomingPage from './component/HomecomingPage';
-
-export default function Page() {
- return ;
-}
\ No newline at end of file
diff --git a/src/app/layout.js b/src/app/layout.js
deleted file mode 100644
index e44ce9e..0000000
--- a/src/app/layout.js
+++ /dev/null
@@ -1,140 +0,0 @@
-import * as React from "react";
-import { Suspense } from "react";
-import { ErrorBoundary } from "react-error-boundary";
-import localFont from "next/font/local";
-import Script from 'next/script';
-import { NextUIProvider } from "@nextui-org/react";
-import DevHeader from "@/components/ui/common/DevHeader";
-
-// components
-import Loading from "@/app/loading";
-import Error from "@/app/error";
-import { AuthProvider } from '@/context/AuthProvider';
-
-// css
-import "@/styles/globals.css";
-
-const pretendard = localFont({
- src: "../../public/fonts/pretendard/PretendardVariable.woff2",
- display: "swap",
- variable: "--font-pretendard",
- preload: true,
- fallback: ["system-ui", "arial"]
-});
-
-const ocra = localFont({
- src: "../../public/fonts/OCRAExtended.woff2",
- display: "swap",
- variable: "--font-ocra",
- preload: true,
- fallback: ["monospace"]
-});
-
-export const viewport = {
- width: "device-width",
- initialScale: 1.0,
- maximumScale: 1.0,
- minimumScale: 1.0,
- userScalable: true,
- themeColor: "#000000",
- colorScheme: "light dark"
-};
-
-export const metadata = {
- metadataBase: new URL('https://gdgocinha.com'),
- title: {
- template: '%s | GDGoC INHA',
- default: 'GDGoC INHA'
- },
- description: "Google Developer Group on Campus at Inha University",
- manifest: "/manifest.json",
- applicationName: "GDGoC INHA",
- keywords: [
- "google", "education", "technology", "developer", "gdg", "gdsc", "gdgoc",
- "google developer group", "google for developers", "google developer groups on campus",
- "inha", "university", "inha university", "구글", "개발자", "동아리", "인하대",
- "인하대학교", "학생", "프로그래밍", "코딩", "개발", "IT", "테크", "테크동아리",
- "웹개발", "앱개발", "클라우드", "AI", "머신러닝", "안드로이드", "iOS",
- "프론트엔드", "백엔드", "풀스택"
- ],
- authors: [{ name: "GDGoC INHA Tech Team" }],
- creator: "GDGoC INHA Tech Team",
- publisher: "GDGoC INHA Tech Team",
- formatDetection: {
- telephone: true,
- date: true,
- address: true,
- email: true,
- url: true
- },
- icons: {
- icon: [
- { url: "/favicon.ico", sizes: "any", type: "image/x-icon" }
- ],
- apple: [
- { url: "/icons/gdgocIcon/180x180.png", sizes: "180x180", type: "image/png" }
- ],
- shortcut: ["/favicon.ico"]
- },
- appleWebApp: {
- capable: true,
- statusBarStyle: "default",
- title: "GDGoC INHA",
- },
- openGraph: {
- title: "GDGoC INHA Univ.",
- description: "Google Developer Group on Campus at Inha University",
- url: "https://gdgocinha.com",
- siteName: "GDGoC INHA",
- images: [
- {
- url: "https://gdgocinha.com/screenshots/home.png",
- width: 1280,
- height: 720,
- alt: "GDGoC INHA Home"
- }
- ],
- type: "website",
- locale: "ko_KR",
- },
- twitter: {
- card: "summary_large_image",
- title: "GDGoC INHA Univ.",
- description: "Google Developer Group on Campus at Inha University",
- images: ["https://gdgocinha.com/screenshots/home.png"],
- creator: "GDGoC INHA Tech Team",
- },
- verification: {
- // Google Search Console 등에 사용되는 확인 코드가 있다면 추가
- // google: "VERIFICATION_CODE",
- },
- alternates: {
- canonical: 'https://gdgocinha.com',
- languages: {
- 'ko-KR': 'https://gdgocinha.com',
- },
- },
-};
-
-export default function RootLayout({ children }) {
- return (
-
-
-
- {((process.env.NEXT_PUBLIC_APP_ENV ?? process.env.NODE_ENV) !== 'production') && }
-
- }>
-
- } >
- {children}
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..b81ce51
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,180 @@
+import * as React from 'react'
+import { Suspense } from 'react'
+import localFont from 'next/font/local'
+import Script from 'next/script'
+import { NextUIProvider } from '@nextui-org/react'
+import DevHeader from '@/components/ui/common/DevHeader'
+
+// components
+import Loading from '@/app/loading'
+import { AuthProvider } from '@/context/AuthProvider'
+
+// css
+import '@/styles/globals.css'
+
+const pretendard = localFont({
+ src: '../../public/fonts/pretendard/PretendardVariable.woff2',
+ display: 'swap',
+ variable: '--font-local-pretendard',
+ preload: true,
+ weight: '100 900',
+ style: 'normal',
+ fallback: ['system-ui', 'arial']
+})
+
+const googleSansFlex = localFont({
+ src: '../../public/fonts/google-sans-flex/google-sans-flex.woff2',
+ display: 'swap',
+ variable: '--font-local-google-sans-flex',
+ preload: true,
+ weight: '100 1000',
+ style: 'normal',
+ fallback: ['Pretendard', 'sans-serif']
+})
+
+const ocra = localFont({
+ src: '../../public/fonts/OCRAExtended.woff2',
+ display: 'swap',
+ variable: '--font-local-ocra',
+ preload: true,
+ weight: '400',
+ style: 'normal',
+ fallback: ['monospace']
+})
+
+export const viewport = {
+ width: 'device-width',
+ initialScale: 1.0,
+ maximumScale: 1.0,
+ minimumScale: 1.0,
+ userScalable: true,
+ themeColor: '#000000',
+ colorScheme: 'light dark'
+}
+
+export const metadata = {
+ metadataBase: new URL('https://gdgocinha.com'),
+ title: {
+ template: '%s | GDGoC INHA',
+ default: 'GDGoC INHA'
+ },
+ description: 'Google Developer Group on Campus at Inha University',
+ manifest: '/manifest.json',
+ applicationName: 'GDGoC INHA',
+ keywords: [
+ 'google',
+ 'education',
+ 'technology',
+ 'developer',
+ 'gdg',
+ 'gdsc',
+ 'gdgoc',
+ 'google developer group',
+ 'google for developers',
+ 'google developer groups on campus',
+ 'inha',
+ 'university',
+ 'inha university',
+ '구글',
+ '개발자',
+ '동아리',
+ '인하대',
+ '인하대학교',
+ '학생',
+ '프로그래밍',
+ '코딩',
+ '개발',
+ 'IT',
+ '테크',
+ '테크동아리',
+ '웹개발',
+ '앱개발',
+ '클라우드',
+ 'AI',
+ '머신러닝',
+ '안드로이드',
+ 'iOS',
+ '프론트엔드',
+ '백엔드',
+ '풀스택'
+ ],
+ authors: [{ name: 'GDGoC INHA Tech Team' }],
+ creator: 'GDGoC INHA Tech Team',
+ publisher: 'GDGoC INHA Tech Team',
+ formatDetection: {
+ telephone: true,
+ date: true,
+ address: true,
+ email: true,
+ url: true
+ },
+ icons: {
+ icon: [{ url: '/favicon.ico', sizes: 'any', type: 'image/x-icon' }],
+ apple: [{ url: '/icons/gdgocIcon/180x180.png', sizes: '180x180', type: 'image/png' }],
+ shortcut: ['/favicon.ico']
+ },
+ appleWebApp: {
+ capable: true,
+ statusBarStyle: 'default',
+ title: 'GDGoC INHA'
+ },
+ openGraph: {
+ title: 'GDGoC INHA Univ.',
+ description: 'Google Developer Group on Campus at Inha University',
+ url: 'https://gdgocinha.com',
+ siteName: 'GDGoC INHA',
+ images: [
+ {
+ url: 'https://gdgocinha.com/screenshots/home.png',
+ width: 1280,
+ height: 720,
+ alt: 'GDGoC INHA Home'
+ }
+ ],
+ type: 'website',
+ locale: 'ko_KR'
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: 'GDGoC INHA Univ.',
+ description: 'Google Developer Group on Campus at Inha University',
+ images: ['https://gdgocinha.com/screenshots/home.png'],
+ creator: 'GDGoC INHA Tech Team'
+ },
+ verification: {
+ // Google Search Console 등에 사용되는 확인 코드가 있다면 추가
+ // google: "VERIFICATION_CODE",
+ },
+ alternates: {
+ canonical: 'https://gdgocinha.com',
+ languages: {
+ 'ko-KR': 'https://gdgocinha.com'
+ }
+ }
+}
+
+export default function RootLayout({ children }) {
+ return (
+
+
+
+ {(process.env.NEXT_PUBLIC_APP_ENV ?? process.env.NODE_ENV) !== 'production' && (
+
+ )}
+
+
+ }>{children}
+
+
+
+
+ )
+}
diff --git a/src/app/loading.js b/src/app/loading.tsx
similarity index 100%
rename from src/app/loading.js
rename to src/app/loading.tsx
diff --git a/src/app/login/layout.tsx b/src/app/login/layout.tsx
new file mode 100644
index 0000000..1964526
--- /dev/null
+++ b/src/app/login/layout.tsx
@@ -0,0 +1,15 @@
+import type { Metadata } from 'next'
+import type { ReactNode } from 'react';
+
+export const metadata: Metadata = {
+ title: '로그인',
+ description: 'GDGoC INHA 계정으로 로그인하세요.',
+};
+
+export default function LoginLayout({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx
new file mode 100644
index 0000000..a05b5e0
--- /dev/null
+++ b/src/app/login/page.tsx
@@ -0,0 +1,302 @@
+'use client'
+
+import { useCallback, useEffect, useMemo, useState } from 'react'
+import axios from 'axios'
+import { useRouter, useSearchParams } from 'next/navigation'
+
+import Loader from '@/components/ui/common/Loader'
+import { GdgGoogleLoginButton, GdgLogo } from '@/components/ui/design-system'
+import { useAuth } from '@/hooks/useAuth'
+import {
+ type LoginApiResponseBody,
+ type LoginExistingUserResponse,
+ type LoginNewUserResponse,
+ loginWithGoogleIdToken
+} from '@/services/auth/authClient'
+import { PENDING_SIGNUP_STORAGE_KEY, type PendingSignupPayload } from '@/constant/auth'
+import { unwrapApiResponse } from '@/utils/api/unwrap'
+
+const DEFAULT_FALLBACK_ROUTE = '/'
+
+const getSafeNextUrl = (raw: string | null): string => {
+ if (!raw) return DEFAULT_FALLBACK_ROUTE
+
+ try {
+ const decoded = decodeURIComponent(raw)
+ return decoded.startsWith('/') ? decoded : DEFAULT_FALLBACK_ROUTE
+ } catch {
+ return DEFAULT_FALLBACK_ROUTE
+ }
+}
+
+const isNewUserResponse = (payload: LoginApiResponseBody): payload is LoginNewUserResponse =>
+ payload.isNewUser === true
+
+const isExistingUserResponse = (
+ payload: LoginApiResponseBody
+): payload is LoginExistingUserResponse => payload.isNewUser === false
+
+interface GoogleStatePayload {
+ next?: string
+ ts?: number
+}
+
+const GOOGLE_OIDC_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth'
+
+const serializeState = (payload: GoogleStatePayload): string => {
+ try {
+ return encodeURIComponent(JSON.stringify(payload))
+ } catch {
+ return ''
+ }
+}
+
+const deserializeState = (raw: string | null): GoogleStatePayload | null => {
+ if (!raw) return null
+ try {
+ return JSON.parse(decodeURIComponent(raw)) as GoogleStatePayload
+ } catch {
+ return null
+ }
+}
+
+const generateNonce = (): string => {
+ if (typeof window === 'undefined' || !window.crypto?.getRandomValues) {
+ return Math.random().toString(36).slice(2)
+ }
+ const buffer = new Uint8Array(16)
+ window.crypto.getRandomValues(buffer)
+ return Array.from(buffer, (byte) => byte.toString(16).padStart(2, '0')).join('')
+}
+
+export default function LoginPage() {
+ const router = useRouter()
+ const searchParams = useSearchParams()
+ const { user, setUser } = useAuth()
+
+ const baseNextUrl = useMemo(
+ () => getSafeNextUrl(searchParams?.get('next') ?? null),
+ [searchParams]
+ )
+ const [nextOverride, setNextOverride] = useState(null)
+ const nextUrl = nextOverride ?? baseNextUrl
+
+ const [loading, setLoading] = useState(false)
+ const [errorMessage, setErrorMessage] = useState(null)
+
+ const googleClientId =
+ process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_CLIENT_ID ??
+ process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID ??
+ ''
+ const googleRedirectUri = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI ?? ''
+ const canUseGoogleLogin = Boolean(googleClientId)
+
+ useEffect(() => {
+ if (user) {
+ router.replace(nextUrl)
+ }
+ }, [user, nextUrl, router])
+
+ const persistPendingSignup = useCallback((payload: PendingSignupPayload) => {
+ if (typeof window === 'undefined') return
+ sessionStorage.setItem(PENDING_SIGNUP_STORAGE_KEY, JSON.stringify(payload))
+ }, [])
+
+ const handleIdTokenLogin = useCallback(
+ async (idToken: string, overrideNextUrl?: string) => {
+ const targetNext = overrideNextUrl ?? nextUrl
+ setLoading(true)
+ setErrorMessage(null)
+
+ try {
+ const response = await loginWithGoogleIdToken(idToken)
+ const data = unwrapApiResponse(response.data)
+
+ if (!data) {
+ throw new Error('로그인 응답을 해석할 수 없습니다.')
+ }
+
+ if (isNewUserResponse(data)) {
+ persistPendingSignup({
+ oauthSubject: data.oauthSubject,
+ email: data.email,
+ name: data.name,
+ picture: data.picture,
+ next: targetNext
+ })
+ try {
+ alert('신규 이용자입니다! 회원가입 페이지로 이동합니다.')
+ } catch {
+ // ignore
+ }
+ const signupRoute = targetNext
+ ? `/signup?next=${encodeURIComponent(targetNext)}`
+ : '/signup'
+ router.replace(signupRoute)
+ return
+ }
+ if (!isExistingUserResponse(data)) {
+ throw new Error('알 수 없는 로그인 응답입니다.')
+ }
+
+ if (data.user && data.accessToken && data.refreshToken) {
+ console.log('[LoginPage] Existing user login success, saving to storage')
+ setUser(data.user, data.accessToken, data.refreshToken)
+ } else {
+ console.warn('[LoginPage] Login success but missing data', {
+ hasUser: !!data.user,
+ hasAt: !!data.accessToken,
+ hasRt: !!data.refreshToken
+ })
+ }
+ if (typeof window !== 'undefined') {
+ sessionStorage.removeItem(PENDING_SIGNUP_STORAGE_KEY)
+ }
+
+ router.replace(targetNext)
+ } catch (err) {
+ let message = '로그인 중 문제가 발생했습니다. 잠시 후 다시 시도해주세요.'
+ if (axios.isAxiosError(err)) {
+ if (err.response?.status === 403) {
+ message =
+ (err.response.data as { message?: string })?.message ??
+ '인하대학교(@inha.edu) 계정으로만 이용할 수 있습니다.'
+ } else if (typeof err.response?.data?.message === 'string') {
+ message = err.response.data.message
+ }
+ } else if (err instanceof Error) {
+ message = err.message
+ }
+ setErrorMessage(message)
+ } finally {
+ setLoading(false)
+ }
+ },
+ [nextUrl, persistPendingSignup, router, setUser]
+ )
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return
+ const hash = window.location.hash
+ if (!hash) return
+ const params = new URLSearchParams(hash.replace(/^#/, ''))
+ const encodedState = params.get('state')
+ const parsedState = deserializeState(encodedState)
+ const stateNext = parsedState?.next ? getSafeNextUrl(parsedState.next) : null
+ if (parsedState?.next) {
+ setNextOverride(stateNext)
+ }
+
+ const idToken = params.get('id_token')
+ const error = params.get('error')
+
+ window.history.replaceState(null, '', window.location.pathname + window.location.search)
+
+ if (idToken) {
+ void handleIdTokenLogin(idToken, stateNext ?? undefined)
+ } else if (error) {
+ setErrorMessage(`Google 로그인 실패: ${error}`)
+ }
+ }, [handleIdTokenLogin])
+
+ const handleGoogleLogin = useCallback(() => {
+ if (!googleClientId) {
+ setErrorMessage('Google Client ID가 설정되어 있지 않습니다. 관리자에게 문의해주세요.')
+ return
+ }
+
+ const redirectUri =
+ googleRedirectUri ||
+ (typeof window !== 'undefined' ? `${window.location.origin}/login` : '/login')
+
+ const nonce = generateNonce()
+ const state = serializeState({ next: nextUrl, ts: Date.now() })
+ const scope = encodeURIComponent('openid email profile')
+ const url = `${GOOGLE_OIDC_ENDPOINT}?client_id=${encodeURIComponent(
+ googleClientId
+ )}&redirect_uri=${encodeURIComponent(
+ redirectUri
+ )}&response_type=id_token&scope=${scope}&nonce=${encodeURIComponent(
+ nonce
+ )}&state=${state}&prompt=select_account`
+
+ setErrorMessage(null)
+ window.location.href = url
+ }, [googleClientId, googleRedirectUri, nextUrl])
+
+ const RenderPcView = () => (
+
+
+
+
+
+
+
+
+
방문을 환영합니다!
+
+ GDGoC INHA 홈페이지를 이용하려면 로그인하세요.
+
+
+
+
+
+
@inha.edu 계정만 사용 가능합니다
+ {errorMessage ?
{errorMessage}
: null}
+
+
+
+
+
+
+ )
+
+ const RenderMobileView = () => (
+
+
+
+
+
+
+
+
+
방문을 환영합니다!
+
GDGoC INHA 홈페이지를 이용하려면 로그인하세요.
+
+
+
+
+
@inha.edu 계정만 사용 가능합니다
+ {errorMessage ?
{errorMessage}
: null}
+
+
+
+
+
+
+ )
+
+ return (
+ <>
+
+
+ >
+ )
+}
diff --git a/src/app/main/layout.js b/src/app/main/layout.js
deleted file mode 100644
index 0fdcd2a..0000000
--- a/src/app/main/layout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import MenuHeader from "@/components/ui/common/MenuHeader";
-import ApiCodeGuard from '@/components/auth/ApiCodeGuard.jsx';
-
-export const metadata = {
- title: "Home", description: "Home management and participation platform",
-};
-
-export default function HomeLayout({children}) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/main/page.jsx b/src/app/main/page.jsx
deleted file mode 100644
index cf4706e..0000000
--- a/src/app/main/page.jsx
+++ /dev/null
@@ -1,52 +0,0 @@
-"use client";
-
-import MainCarousel from '@/components/main/MainCarousel';
-import CardCarousel from '@/components/main/CardCarousel';
-import EventCard from '@/components/main/EventCard';
-import FAQ from '@/components/main/FAQ';
-
-import { mainSlides, ongoingEvents, ongoingStudies } from '@/mock/events';
-
-export default function Page() {
- // 이미지 임포트 문제 해결을 위한 실제 슬라이드 데이터
-
-
- // 카드 아이템을 SplideSlide로 감싸는 함수
- const renderEventCards = (events) => {
- return events.map((event, index) => (
-
-
-
- ));
- };
-
- return (
-
-
-
-
-
-
- {renderEventCards(ongoingEvents)}
-
-
-
- {renderEventCards(ongoingStudies)}
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/manitto/admin/layout.js b/src/app/manitto/admin/layout.js
deleted file mode 100644
index 7df267a..0000000
--- a/src/app/manitto/admin/layout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import MenuHeader from "@/components/ui/common/MenuHeader";
-import ApiCodeGuard from "@/components/auth/ApiCodeGuard.jsx";
-
-export const metadata = {
- title: "Manitto Admin", description: "GDGoC INHA Manitto Management",
-};
-
-export default function ManittoAdminLayout({children}) {
- return (
- <>
-
- {children}
- >
- );
-}
\ No newline at end of file
diff --git a/src/app/manitto/admin/page.jsx b/src/app/manitto/admin/page.jsx
deleted file mode 100644
index 7f0e6dc..0000000
--- a/src/app/manitto/admin/page.jsx
+++ /dev/null
@@ -1,370 +0,0 @@
-'use client';
-
-import {useEffect, useState} from 'react';
-import {Button, Card, CardBody, CardHeader, Divider, Input} from '@nextui-org/react';
-import {useAuthenticatedApi} from '@/hooks/useAuthenticatedApi';
-
-export default function ManitoAdminPage() {
- const {apiClient} = useAuthenticatedApi();
-
- // 공통
- const [sessionCode, setSessionCode] = useState('');
-
- // 세션 관리용
- const [sessions, setSessions] = useState([]); // [{id, code, name, createdAt,...}]
- const [newSessionCode, setNewSessionCode] = useState('');
- const [newSessionTitle, setNewSessionTitle] = useState('');
- const [loadingSessions, setLoadingSessions] = useState(false);
-
- // 파일
- const [participantsFile, setParticipantsFile] = useState(null);
- const [encryptedFile, setEncryptedFile] = useState(null);
-
- // 로딩
- const [loadingParticipants, setLoadingParticipants] = useState(false);
- const [loadingEncrypted, setLoadingEncrypted] = useState(false);
-
- // 메시지
- const [message, setMessage] = useState('');
- const [error, setError] = useState('');
-
- const resetMessages = () => {
- setMessage('');
- setError('');
- };
-
- /** ====== 세션 목록 불러오기 ====== */
- const fetchSessions = async () => {
- try {
- setLoadingSessions(true);
- const res = await apiClient.get('/admin/manito/sessions');
- // ApiResponse 가정: { data: [ ... ] }
- const list = res.data?.data ?? [];
- setSessions(Array.isArray(list) ? list : []);
- } catch (e) {
- console.error(e);
- setError('세션 목록을 불러오는 중 오류가 발생했습니다.');
- } finally {
- setLoadingSessions(false);
- }
- };
-
- useEffect(() => {
- fetchSessions();
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, []);
-
- /** ====== 세션 생성 ====== */
- const handleCreateSession = async () => {
- resetMessages();
- const code = newSessionCode.trim();
- const title = newSessionTitle.trim();
-
- if (!code) {
- setError('세션 코드를 입력해 주세요.');
- return;
- }
- if (!title) {
- setError('세션 이름을 입력해 주세요.');
- return;
- }
-
- try {
- setLoadingSessions(true);
- await apiClient.post('/admin/manito/sessions', {code, title});
-
- // 세션 목록 갱신
- await fetchSessions();
- // 공통 sessionCode 에도 세팅
- setSessionCode(code);
- setNewSessionCode('');
- setNewSessionTitle('');
- setMessage(`세션이 생성되었습니다. (code: ${code})`);
- } catch (e) {
- console.error(e);
- setError('세션 생성 중 오류가 발생했습니다.');
- } finally {
- setLoadingSessions(false);
- }
- };
-
- /** ===== 파일 핸들러 ===== */
- const handleParticipantsFileChange = (e) => {
- resetMessages();
- const file = e.target.files?.[0] ?? null;
- setParticipantsFile(file);
- };
-
- const handleEncryptedFileChange = (e) => {
- resetMessages();
- const file = e.target.files?.[0] ?? null;
- setEncryptedFile(file);
- };
-
- /** ===== 다운로드 공통 util ===== */
- const downloadBlob = (blob, filename) => {
- const url = window.URL.createObjectURL(blob);
- const a = document.createElement('a');
- a.href = url;
- a.download = filename;
- document.body.appendChild(a);
- a.click();
- a.remove();
- window.URL.revokeObjectURL(url);
- };
-
- /** ===== 1단계: 참가자 CSV 업로드 → 매칭 CSV 다운로드 ===== */
- const handleUploadParticipants = async () => {
- resetMessages();
-
- if (!sessionCode.trim()) {
- setError('세션 코드를 선택하거나 입력해 주세요.');
- return;
- }
- if (!participantsFile) {
- setError('참가자 CSV 파일을 선택해 주세요.');
- return;
- }
-
- try {
- setLoadingParticipants(true);
-
- const formData = new FormData();
- formData.append('sessionCode', sessionCode.trim());
- formData.append('file', participantsFile);
-
- const res = await apiClient.post('/admin/manito/upload', formData, {
- responseType: 'blob',
- });
-
- const blob = res.data;
- const disposition = res.headers?.['content-disposition'] || '';
- let filename = `manito-${sessionCode.trim()}.csv`;
- const match = disposition.match(/filename="?([^"]+)"?/);
- if (match && match[1]) {
- filename = match[1];
- }
-
- downloadBlob(blob, filename);
- setMessage('참가자 CSV 업로드 및 매칭 CSV 다운로드가 완료되었습니다.');
- } catch (e) {
- console.error(e);
- setError('참가자 CSV 업로드 또는 매칭 CSV 다운로드 중 오류가 발생했습니다.');
- } finally {
- setLoadingParticipants(false);
- }
- };
-
- /** ===== 2단계: 암호문 CSV 업로드 ===== */
- const handleUploadEncrypted = async () => {
- resetMessages();
-
- if (!sessionCode.trim()) {
- setError('세션 코드를 선택하거나 입력해 주세요.');
- return;
- }
- if (!encryptedFile) {
- setError('암호문 CSV 파일을 선택해 주세요.');
- return;
- }
-
- try {
- setLoadingEncrypted(true);
-
- const formData = new FormData();
- formData.append('sessionCode', sessionCode.trim());
- formData.append('file', encryptedFile);
-
- const res = await apiClient.post('/admin/manito/upload-encrypted', formData);
- const body = res.data;
- setMessage(body?.message || '암호문 CSV 업로드가 완료되었습니다.');
- } catch (e) {
- console.error(e);
- setError('암호문 CSV 업로드 중 오류가 발생했습니다.');
- } finally {
- setLoadingEncrypted(false);
- }
- };
-
- return (
-
마니또 관리(Admin)
-
- {/* 공통 설정 + 세션 등록/선택 */}
-
-
- 공통 설정 · 세션 관리
-
- 세션 단위로 참가자/매칭/암호문을 관리합니다.
-
- 먼저 세션을 생성한 뒤, 해당 세션을 선택하고 아래 단계를 진행하세요.
-
-
-
-
- {/* 현재 사용 세션 코드 (직접 입력/수정 가능) */}
- setSessionCode(e.target.value)}
- variant="bordered"
- classNames={{
- label: 'text-zinc-300',
- input: 'text-white',
- inputWrapper: 'bg-zinc-900 border-zinc-700 group-data-[focus=true]:border-zinc-400',
- }}
- />
-
-
-
- {/* 새 세션 생성 */}
-
-
새 세션 등록
-
- setNewSessionCode(e.target.value)}
- variant="bordered"
- classNames={{
- label: 'text-zinc-300',
- input: 'text-white',
- inputWrapper: 'bg-zinc-900 border-zinc-700 group-data-[focus=true]:border-zinc-400',
- }}
- />
- setNewSessionTitle(e.target.value)}
- variant="bordered"
- classNames={{
- label: 'text-zinc-300',
- input: 'text-white',
- inputWrapper: 'bg-zinc-900 border-zinc-700 group-data-[focus=true]:border-zinc-400',
- }}
- />
-
-
- 새 세션 생성
-
-
-
- {/* 세션 목록 */}
-
-
-
세션 목록
-
- {loadingSessions && (
세션 목록을 불러오는 중...
)}
- {!loadingSessions && sessions.length === 0 && (
- 등록된 세션이 없습니다. 위에서 새 세션을 생성해 주세요.
-
)}
- {sessions.map((s) => (
-
-
- {s.title || '(이름 없음)'}
-
-
- code: {s.code}
-
-
-
setSessionCode(s.code)}
- >
- 사용
-
-
))}
-
-
-
-
-
- {/* 1단계: 참가자 CSV 업로드 */}
-
-
-
- 1단계 · 참가자 CSV 업로드 & 매칭 생성
-
-
- CSV 헤더: studentId,name,pin · 업로드 후, 서버에서 매칭을 생성하고
-
- giverStudentId,giverName,receiverStudentId,receiverName CSV를
- 바로 다운로드합니다.
-
-
-
-
-
-
- 참가자 CSV 업로드 & 매칭 CSV 다운로드
-
-
-
-
- {/* 2단계: 암호문 CSV 업로드 */}
-
-
-
- 2단계 · 암호문(encryptedManitto) CSV 업로드
-
-
- 클라이언트에서 매칭 CSV를 기반으로 암호화한 결과를 업로드합니다.
-
- CSV 헤더 예시: studentId,encryptedManitto
-
-
-
-
-
-
- 암호문 CSV 업로드
-
-
-
-
- {(message || error) && (
-
- {message && ({message}
)}
- {error && {error}
}
-
- )}
-
);
-}
\ No newline at end of file
diff --git a/src/app/manitto/layout.js b/src/app/manitto/layout.js
deleted file mode 100644
index 938cf37..0000000
--- a/src/app/manitto/layout.js
+++ /dev/null
@@ -1,12 +0,0 @@
-export const metadata = {
- title: '마니또 확인',
- description: '마니또 매칭 결과를 확인합니다.',
-};
-
-export default function ManitoLayout({ children }) {
- return (
-
- {children}
-
- );
-}
\ No newline at end of file
diff --git a/src/app/manitto/page.jsx b/src/app/manitto/page.jsx
deleted file mode 100644
index 58aa9a8..0000000
--- a/src/app/manitto/page.jsx
+++ /dev/null
@@ -1,247 +0,0 @@
-'use client';
-
-import {useEffect, useState} from 'react';
-import {useSearchParams} from 'next/navigation';
-import {Button, Card, CardBody, CardHeader, Divider, Input} from '@nextui-org/react';
-import {useAuthenticatedApi} from '@/hooks/useAuthenticatedApi';
-
-export default function ManitoVerifyPage() {
- const searchParams = useSearchParams();
- const {apiClient} = useAuthenticatedApi();
-
- const [sessionCode, setSessionCode] = useState('');
- const [studentId, setStudentId] = useState('');
- const [hash, setHash] = useState('');
-
- const [pin, setPin] = useState('');
- const [loading, setLoading] = useState(false);
- const [error, setError] = useState('');
- const [cipher, setCipher] = useState('');
- const [plain, setPlain] = useState(null); // { receiverStudentId, receiverName }
- const [ownerName, setOwnerName] = useState(''); // 요청자 이름 (API에서 내려주는 값 가정)
-
- useEffect(() => {
- if (!searchParams) return;
- const sCode = searchParams.get('sessionCode') || '';
- const sId = searchParams.get('studentId') || '';
- const h = searchParams.get('hash') || '';
-
- setSessionCode(sCode);
- setStudentId(sId);
- setHash(h);
- }, [searchParams]);
-
- /** ========= Base64URL → Uint8Array ========= */
- const base64UrlToBytes = (str) => {
- if (!str) return new Uint8Array();
- let base64 = str.replace(/-/g, '+').replace(/_/g, '/');
- while (base64.length % 4 !== 0) base64 += '=';
- const binary = typeof atob !== 'undefined' ? atob(base64) : Buffer.from(base64, 'base64').toString('binary');
-
- const bytes = new Uint8Array(binary.length);
- for (let i = 0; i < binary.length; i++) {
- bytes[i] = binary.charCodeAt(i);
- }
- return bytes;
- };
-
- /** ========= 실제 복호화 로직 (AES-256-GCM) =========
- * encrypted: base64url(nonce(12) + ciphertext+tag)
- * hashKey: base64url(32바이트 랜덤) → 그대로 AES 키
- */
- const tryDecrypt = async (encrypted, hashKey) => {
- if (!encrypted || !hashKey) return null;
- if (typeof window === 'undefined' || !window.crypto?.subtle) return null;
-
- try {
- const combined = base64UrlToBytes(encrypted);
- if (combined.length <= 12) return null;
-
- const nonce = combined.slice(0, 12);
- const ciphertext = combined.slice(12);
-
- const keyBytes = base64UrlToBytes(hashKey);
- if (keyBytes.length !== 32) {
- console.warn('Unexpected key length:', keyBytes.length);
- return null;
- }
-
- const key = await window.crypto.subtle.importKey('raw', keyBytes, {name: 'AES-GCM'}, false, ['decrypt'],);
-
- const plainBuffer = await window.crypto.subtle.decrypt({name: 'AES-GCM', iv: nonce}, key, ciphertext,);
-
- const dec = new TextDecoder();
- const jsonStr = dec.decode(plainBuffer);
- return JSON.parse(jsonStr);
- } catch (e) {
- console.error('Manito decrypt error:', e);
- return null;
- }
- };
-
- /** 🔥 encrypted(cipher) 값이 세팅되면 hash로 자동 복호화 */
- useEffect(() => {
- if (!cipher || !hash) {
- setPlain(null);
- return;
- }
-
- let cancelled = false;
-
- (async () => {
- const decoded = await tryDecrypt(cipher, hash);
- if (cancelled) return;
-
- if (decoded) {
- setPlain(decoded);
- } else {
- // 여기서도 error 세팅
- setError((prev) => (prev ? prev + '\n' : '') + '복호화에 실패했습니다. 해시 값이 올바른지 확인해 주세요.',);
- setPlain(null);
- }
- })();
-
- return () => {
- cancelled = true;
- };
- }, [cipher, hash]);
-
- const handleSubmit = async (e) => {
- e.preventDefault();
- setError('');
- setCipher('');
- setPlain(null);
- setOwnerName('');
-
- if (!sessionCode || !studentId) {
- setError('세션 코드 또는 학번 정보가 잘못되었습니다. 링크를 다시 확인해 주세요.');
- return;
- }
-
- if (!/^\d{4}$/.test(pin)) {
- setError('PIN은 숫자 4자리여야 합니다.');
- return;
- }
-
- try {
- setLoading(true);
-
- const res = await apiClient.post('/manito/verify', {sessionCode, studentId, pin}, {},);
-
- const body = res.data;
-
- // ✅ 서버 응답 키 이름 맞춰서 사용
- const encrypted = body?.data?.encryptedManito || '';
- const owner = body?.data?.ownerName || ''; // 백엔드에서 내려준다고 가정
-
- setOwnerName(owner);
- setCipher(encrypted); // 🔥 cipher가 바뀌면 useEffect가 hash로 복호화
- } catch (err) {
- const res = err?.response;
- const msg = res?.data?.message || res?.data?.error || err?.message || '알 수 없는 오류가 발생했습니다.';
- setError(msg);
- } finally {
- setLoading(false);
- }
- };
-
- const disabled = !sessionCode || !studentId;
-
- // 결과 문구 구성
- const receiverName = plain?.receiverName;
- const ownerLabel = ownerName || '당신';
-
- return (
-
-
- 🎁 GDGoC INHA · 마니또
-
- 마니또 확인하기
-
-
- 전달받은 링크로 접속한 뒤,
- 본인이 설정한 PIN 4자리를 입력해 주세요.
-
-
-
-
-
-
- {disabled && (
- 필수 정보가 누락되었습니다. 링크를 다시 확인해 주세요.
-
)}
-
-
- setPin(e.target.value.replace(/[^0-9]/g, '').slice(0, 4),)}
- classNames={{
- label: 'text-zinc-300',
- input: 'text-white text-base tracking-[0.25em]',
- inputWrapper: 'bg-zinc-900 border-zinc-700 group-data-[focus=true]:border-zinc-300',
- }}
- isDisabled={disabled || loading}
- />
-
- {error && (
- {error}
-
)}
-
-
- 마니또 확인하기 ✨
-
-
-
- {/* 결과 영역 */}
- {(cipher || plain) && (<>
-
-
-
- 결과
-
-
- {plain ? (
-
- {ownerLabel}
- 님의 마니또는
-
- {receiverName || '알 수 없음'}
-
- 님입니다! 🎉
-
-
- 이 정보는 브라우저에서 해시값을 사용해 복호화한 결과이며,
- 서버에는 평문으로 저장되지 않습니다.
-
-
) : (
-
- 복호화에 실패하였습니다.
-
-
- 해시 값이 올바른지, 전달받은 링크가 정확한지 다시 한 번
- 확인해 주세요. 문제가 계속되면 운영진에게 문의해 주세요.
-
-
)}
-
- >)}
-
-
-
);
-}
\ No newline at end of file
diff --git a/src/app/not-found.js b/src/app/not-found.tsx
similarity index 100%
rename from src/app/not-found.js
rename to src/app/not-found.tsx
diff --git a/src/app/notice/page.jsx b/src/app/notice/page.jsx
deleted file mode 100644
index 6c0573d..0000000
--- a/src/app/notice/page.jsx
+++ /dev/null
@@ -1,81 +0,0 @@
-'use client';
-
-import { useMemo, useState } from 'react';
-import NoticeCard from '@/components/notice/NoticeCard';
-import NoticeModal from '@/components/notice/NoticeModal';
-import { notices, NOTICE_CATEGORIES, NOTICE_STATUSES } from '@/mock/notices';
-
-export default function NoticePage() {
- const [category, setCategory] = useState('all');
- const [status, setStatus] = useState('all');
- const [selected, setSelected] = useState(null);
- const [loading, setLoading] = useState(false);
-
- const filtered = useMemo(() => {
- return notices.filter((n) => {
- const categoryOk = category === 'all' || n.category === category;
- const statusOk = status === 'all' || n.status === status;
- return categoryOk && statusOk;
- });
- }, [category, status]);
-
- if (!loading) {
- return (
-
-
Error
-
- );
- }
-
- return (
-
- {/* Hero */}
-
-
GDGoC INHA에서 진행중인 프로젝트 둘러보기
-
개발자 커뮤니티의 공지, 행사, 프로젝트 소식을 확인하세요.
-
-
- {/* Filter Bar */}
-
-
-
- {NOTICE_CATEGORIES.map((c) => (
- setCategory(c.key)}
- className={`px-4 h-9 rounded-full whitespace-nowrap text-sm font-medium ${category === c.key ? 'bg-white text-black' : 'bg-[#1e1e1e] text-gray-200 hover:bg-[#2a2a2a]'}`}
- >
- {c.label}
-
- ))}
-
-
- {NOTICE_STATUSES.map((s) => (
- setStatus(s.key)}
- className={`px-3 h-9 rounded-full text-sm font-medium flex items-center gap-2 ${status === s.key ? 'bg-white text-black' : 'bg-[#1e1e1e] text-gray-200 hover:bg-[#2a2a2a]'}`}
- >
- {s.key !== 'all' && (
-
- )}
- {s.label}
-
- ))}
-
-
-
-
- {/* Grid */}
-
- {filtered.map((item) => (
-
- ))}
-
-
-
setSelected(null)} />
-
- );
-}
-
-
diff --git a/src/app/page.jsx b/src/app/page.jsx
deleted file mode 100644
index 99c6d27..0000000
--- a/src/app/page.jsx
+++ /dev/null
@@ -1,57 +0,0 @@
-"use client";
-
-import { useEffect } from 'react';
-import { useRouter } from 'next/navigation';
-
-import Lenis from '@studio-freight/lenis';
-
-import gsap from 'gsap';
-import ScrollTrigger from 'gsap/ScrollTrigger';
-
-import HeroSection from '@/components/landing/HeroSection';
-import AboutSection from '@/components/landing/AboutSection';
-import StatsSection from '@/components/landing/StatsSection';
-import ActivitiesSection from '@/components/landing/ActivitiesSection';
-import StudySection from '@/components/landing/StudySection';
-import PartnersSection from '@/components/landing/PartnersSection';
-import CTASection from '@/components/landing/CTASection';
-
-gsap.registerPlugin(ScrollTrigger);
-
-export default function Home() {
-
- const router = useRouter();
-
- useEffect(() => {
- const lenis = new Lenis({
- duration: 1.5,
- smoothWheel: true,
- wheelMultiplier: 0.8,
- smoothTouch: true,
- });
-
- gsap.ticker.add((time) => {
- lenis.raf(time * 800);
- });
-
- lenis.on('scroll', ScrollTrigger.update);
-
- return () => {
- lenis.destroy();
- gsap.ticker.remove();
- };
- }, []);
-
-
- return (
-
- );
-}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..1fa3f3b
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,54 @@
+'use client'
+
+import { useEffect } from 'react'
+import { useRouter } from 'next/navigation'
+
+import Lenis from '@studio-freight/lenis'
+
+import gsap from 'gsap'
+import ScrollTrigger from 'gsap/ScrollTrigger'
+
+import HeroSection from '@/components/landing/HeroSection'
+import AboutSection from '@/components/landing/AboutSection'
+import StatsSection from '@/components/landing/StatsSection'
+import ActivitiesSection from '@/components/landing/ActivitiesSection'
+import PartnersSection from '@/components/landing/PartnersSection'
+import CTASection from '@/components/landing/CTASection'
+
+gsap.registerPlugin(ScrollTrigger)
+
+export default function Home() {
+ const router = useRouter()
+
+ useEffect(() => {
+ const lenis = new Lenis({
+ duration: 1.5,
+ smoothWheel: true,
+ wheelMultiplier: 0.8
+ })
+
+ const tickerHandler = (time: number) => {
+ lenis.raf(time * 800)
+ }
+
+ gsap.ticker.add(tickerHandler)
+
+ lenis.on('scroll', ScrollTrigger.update)
+
+ return () => {
+ lenis.destroy()
+ gsap.ticker.remove(tickerHandler)
+ }
+ }, [])
+
+ return (
+
+ )
+}
diff --git a/src/app/recruit/core/completed/page.tsx b/src/app/recruit/core/completed/page.tsx
new file mode 100644
index 0000000..2981862
--- /dev/null
+++ b/src/app/recruit/core/completed/page.tsx
@@ -0,0 +1,85 @@
+'use client'
+
+import Link from 'next/link'
+import { GdgLogo, GdgButton } from '@/components/ui/design-system'
+
+function Bullet({ children }: { children: React.ReactNode }) {
+ return (
+
+ )
+}
+
+export default function RecruitCoreCompleted() {
+ return (
+
+
+ {/* Header */}
+
+
+
Core Member 지원 완료
+
+
+ {/* Content Sections - Step 2 UI 이관 */}
+
+
+
모집 일정
+
+
+
+
서류 지원 기간
+
2026. 2. 12.(목) - 2026. 3. 14.(토) 23:59
+
+
+
서류 결과 발표
+
~ 2026. 3. 15.(일)
+
+
+
면접 진행 기간
+
2026. 3. 16.(월) - 2026. 3. 20.(금)
+
+ ※ 지원자 및 면접관 일정에 따라 마감 전 면접이 가능할 수 있습니다.
+
+
+
+
최종 결과 발표
+
~ 2026. 3. 21.(토)
+
+
+
+
+
+
+
면접 안내
+
+
원칙적으로 대면 면접을 진행하며, 부득이한 경우 비대면으로 조정될 수 있습니다.
+
+ 면접은 인하대학교 내부 장소에서 진행됩니다.
+
+
+
+
+
+
활동 안내
+
+
운영진으로 활동 시, 매주 1회 정기 운영진 회의에 필수 참석해야 합니다.
+
※ 일정은 3월 내로 공지 드립니다.
+
+
+
+
+
+
+ 홈으로 이동
+
+
+
+
+
+
+ )
+}
diff --git a/src/app/recruit/core/layout.tsx b/src/app/recruit/core/layout.tsx
new file mode 100644
index 0000000..757c9ef
--- /dev/null
+++ b/src/app/recruit/core/layout.tsx
@@ -0,0 +1,15 @@
+import type { Metadata } from 'next'
+import ApiCodeGuard from '@/components/auth/ApiCodeGuard'
+
+export const metadata: Metadata = {
+ title: '운영진 모집',
+ description: 'GDGoC INHA 2026-1 운영진(CORE) 멤버를 모집합니다.'
+}
+
+export default function RecruitCoreLayout({ children }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/app/recruit/core/page.tsx b/src/app/recruit/core/page.tsx
new file mode 100644
index 0000000..fa928f1
--- /dev/null
+++ b/src/app/recruit/core/page.tsx
@@ -0,0 +1,926 @@
+'use client'
+
+import { useEffect, useMemo, useState, type ChangeEvent, type FormEvent } from 'react'
+import { useRouter } from 'next/navigation'
+
+import {
+ GdgButton,
+ GdgCheckbox,
+ GdgFieldContainer,
+ GdgFileCard,
+ GdgLogo,
+ GdgMajorDropdown,
+ GdgTextarea,
+ GdgUploadButton,
+ GdgInputField
+} from '@/components/ui/design-system'
+import { PrivacyPolicyNotice } from '@/components/ui/common/PrivacyPolicyNotice'
+import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi'
+import { cn } from '@/utils/cn'
+import { formatPhoneNumberInput, toPhoneDigits } from '@/utils/phoneNumber'
+
+type RecruitStep = 0 | 1 | 2 | 3
+
+type FormFile = {
+ name: string
+ size: number
+ file?: File
+}
+
+type RecruitFormData = {
+ name: string
+ studentId: string
+ email: string
+ major: string
+ phone: string
+ team: string
+ motivation: string
+ role: string
+ strength: string
+ determination: string
+ files: FormFile[]
+}
+
+type PrefillPayload = {
+ name?: string
+ studentId?: string
+ email?: string
+ major?: string
+ phone?: string
+}
+
+const STEPS = ['기본정보', '내용작성', '일정안내', '약관동의'] as const
+const TEAM_OPTIONS = [
+ { id: 'BD', label: 'BD' },
+ { id: 'HR', label: 'HR' },
+ { id: 'TECH', label: 'TECH' },
+ { id: 'PR/DESIGN', label: 'PR·DESIGN' }
+] as const
+
+const unwrapPrefill = (raw: unknown): PrefillPayload | null => {
+ if (!raw || typeof raw !== 'object') return null
+
+ const record = raw as Record
+ if ('data' in record && record.data && typeof record.data === 'object') {
+ return unwrapPrefill(record.data)
+ }
+
+ const readString = (key: keyof PrefillPayload) => {
+ const value = record[key]
+ return typeof value === 'string' ? value : undefined
+ }
+
+ return {
+ name: readString('name'),
+ studentId: readString('studentId'),
+ email: readString('email'),
+ major: readString('major'),
+ phone: readString('phone')
+ }
+}
+
+const formatFileSize = (bytes: number) => {
+ if (!bytes) return '0.00mb'
+ const mb = bytes / 1024 / 1024
+ return `${mb.toFixed(2)}mb`
+}
+
+function StepBar({
+ currentStep,
+ maxReachedStep,
+ onStepClick
+}: {
+ currentStep: RecruitStep
+ maxReachedStep: RecruitStep
+ onStepClick: (step: RecruitStep) => void
+}) {
+ return (
+
+ {STEPS.map((step, index) => {
+ const stepIndex = index as RecruitStep
+ const isCurrent = stepIndex === currentStep
+ const isActivated = stepIndex <= maxReachedStep
+ const isClickable = stepIndex <= maxReachedStep
+ const isLast = index === STEPS.length - 1
+
+ return (
+
+
onStepClick(stepIndex)}
+ disabled={!isClickable}
+ className={cn(
+ 'rounded-full border bg-black px-3 py-1.5 whitespace-nowrap typo-pc-b3 mobile:typo-m-c1 transition-colors',
+ isCurrent && 'bg-red border-red text-white',
+ !isCurrent && isActivated && 'border-red text-white',
+ !isCurrent && !isActivated && 'border-gray-400 text-gray-400',
+ isClickable ? 'cursor-pointer' : 'cursor-not-allowed'
+ )}
+ >
+ {step}
+
+ {!isLast && (
+
+ )}
+
+ )
+ })}
+
+ )
+}
+
+function TextareaField({
+ name,
+ label,
+ value,
+ maxLength,
+ rows,
+ required,
+ error,
+ helper,
+ onChange
+}: {
+ name: keyof RecruitFormData
+ label: string
+ value: string
+ maxLength: number
+ rows: number
+ required?: boolean
+ error?: boolean
+ helper?: string
+ onChange: (event: ChangeEvent) => void
+}) {
+ return (
+
+
+
+ )
+}
+
+function Bullet({ children }: { children: React.ReactNode }) {
+ return (
+
+ )
+}
+
+export default function RecruitCore() {
+ const router = useRouter()
+ const { apiClient } = useAuthenticatedApi()
+
+ const [currentStep, setCurrentStep] = useState(0)
+ const [maxReachedStep, setMaxReachedStep] = useState(0)
+ const [errors, setErrors] = useState>({})
+ const [prefillError, setPrefillError] = useState(null)
+ const [scheduleChecked, setScheduleChecked] = useState(false)
+ const [agreementChecked, setAgreementChecked] = useState(false)
+
+ const [formData, setFormData] = useState({
+ name: '',
+ studentId: '',
+ email: '',
+ major: '',
+ phone: '',
+ team: '',
+ motivation: '',
+ role: '',
+ strength: '',
+ determination: '',
+ files: []
+ })
+
+ const isCurrentStepValid = useMemo(() => {
+ if (currentStep === 0) {
+ return Boolean(
+ formData.name.trim() &&
+ formData.studentId.trim() &&
+ formData.email.trim() &&
+ formData.major.trim() &&
+ formData.phone.trim()
+ )
+ }
+ if (currentStep === 1) {
+ return Boolean(
+ formData.team.trim() &&
+ formData.motivation.trim() &&
+ formData.role.trim() &&
+ formData.strength.trim() &&
+ formData.determination.trim()
+ )
+ }
+ if (currentStep === 2) return scheduleChecked
+ if (currentStep === 3) return agreementChecked
+ return false
+ }, [currentStep, formData, scheduleChecked, agreementChecked])
+
+ useEffect(() => {
+ let active = true
+
+ const fetchPrefill = async () => {
+ try {
+ const response = await apiClient.get('/recruit/core/prefill')
+ if (!active) return
+
+ const payload = unwrapPrefill(response.data)
+ if (!payload) return
+
+ setFormData((prev) => ({
+ ...prev,
+ name: prev.name || payload.name || '',
+ studentId: prev.studentId || payload.studentId || '',
+ email: prev.email || payload.email || '',
+ major: prev.major || payload.major || '',
+ phone: formatPhoneNumberInput(prev.phone || payload.phone || '')
+ }))
+ } catch (error: any) {
+ if (!active) return
+ const errorCode = error.response?.data?.code
+ if (errorCode === 'ALREADY_APPLIED') {
+ alert('이미 지원이 완료되었습니다. 제출된 지원서는 마이페이지에서 확인하실 수 있습니다.')
+ router.replace('/recruit/core/completed')
+ } else {
+ console.error('[코어 리크루팅] 기본 정보 불러오기에 실패했습니다.', error)
+ setPrefillError('기본 정보를 불러오지 못했습니다. 직접 입력해주세요.')
+ }
+ }
+ }
+
+ void fetchPrefill()
+
+ return () => {
+ active = false
+ }
+ }, [apiClient, router])
+
+ const validateStep = (step: RecruitStep) => {
+ const nextErrors: Record = {}
+
+ if (step === 0) {
+ if (!formData.name.trim()) nextErrors.name = true
+ if (!formData.studentId.trim()) nextErrors.studentId = true
+ if (!formData.email.trim()) nextErrors.email = true
+ if (!formData.major.trim()) nextErrors.major = true
+ if (!formData.phone.trim()) nextErrors.phone = true
+ }
+
+ if (step === 1) {
+ if (!formData.team.trim()) nextErrors.team = true
+ if (!formData.motivation.trim()) nextErrors.motivation = true
+ if (!formData.role.trim()) nextErrors.role = true
+ if (!formData.strength.trim()) nextErrors.strength = true
+ if (!formData.determination.trim()) nextErrors.determination = true
+ }
+
+ if (step === 2 && !scheduleChecked) nextErrors.scheduleCheck = true
+ if (step === 3 && !agreementChecked) nextErrors.agreementCheck = true
+
+ setErrors(nextErrors)
+ return Object.keys(nextErrors).length === 0
+ }
+
+ const handleInputValueChange = (name: keyof RecruitFormData, value: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ [name]: name === 'phone' ? formatPhoneNumberInput(value) : value
+ }))
+ }
+
+ const handleTextareaChange = (event: ChangeEvent) => {
+ const { name, value } = event.target
+
+ setFormData((prev) => ({
+ ...prev,
+ [name]: value
+ }))
+ }
+
+ const handleTeamChange = (team: string) => {
+ setFormData((prev) => ({
+ ...prev,
+ team
+ }))
+
+ setErrors((prev) => {
+ if (!prev.team) return prev
+ const next = { ...prev }
+ delete next.team
+ return next
+ })
+ }
+
+ const handleFileInput = (event: ChangeEvent) => {
+ const files = event.target.files
+ if (!files) return
+
+ const nextFiles = Array.from(files).map((file) => ({
+ name: file.name,
+ size: file.size,
+ file
+ }))
+
+ setFormData((prev) => ({
+ ...prev,
+ files: [...prev.files, ...nextFiles]
+ }))
+
+ event.target.value = ''
+ }
+
+ const handleRemoveFile = (targetIndex: number) => {
+ setFormData((prev) => ({
+ ...prev,
+ files: prev.files.filter((_, index) => index !== targetIndex)
+ }))
+ }
+
+ const handlePrevious = () => {
+ if (currentStep === 0) return
+ setErrors({})
+ setCurrentStep((currentStep - 1) as RecruitStep)
+ }
+
+ const handleStepClick = (step: RecruitStep) => {
+ if (step > maxReachedStep) return
+ setErrors({})
+ setCurrentStep(step)
+ }
+
+ const handleSubmit = async (event: FormEvent) => {
+ event.preventDefault()
+
+ if (!validateStep(currentStep)) return
+
+ if (currentStep < 3) {
+ const nextStep = (currentStep + 1) as RecruitStep
+ setCurrentStep(nextStep)
+ setMaxReachedStep((prev) => (nextStep > prev ? nextStep : prev))
+ setErrors({})
+ return
+ }
+
+ try {
+ const payload = {
+ snapshot: {
+ name: formData.name,
+ studentId: formData.studentId,
+ phone: toPhoneDigits(formData.phone),
+ major: formData.major,
+ email: formData.email
+ },
+ team: formData.team,
+ motivation: formData.motivation,
+ wish: formData.role,
+ strengths: formData.strength,
+ pledge: formData.determination,
+ fileUrls: []
+ }
+
+ await apiClient.post('/recruit/core/applications', payload)
+ alert('지원서가 제출되었습니다!')
+ router.replace('/recruit/core/completed')
+ } catch (error: any) {
+ if (error?.response?.status === 409) {
+ alert('이미 지원이 완료되었습니다.')
+ router.replace('/recruit/core/completed')
+ } else {
+ alert('지원서 제출 중 오류가 발생했습니다.')
+ }
+ }
+ }
+
+ return (
+
+
+
+
+
+
Core Member 지원
+
+
+
+
+
+
+ {currentStep === 0 ? (
+ <>
+
+
+
+ 아래 정보는 회원가입 시 입력한 정보를 기반으로 자동 입력 됩니다.
+
+
+ 지원서 제출 시점의 정보를 기준으로 저장 됩니다.
+
+
+ 예상 소요 시간: 약 10~15분
+
+
+
+
+ {prefillError ? (
+
{prefillError}
+ ) : null}
+
+
+
+
+
+ handleInputValueChange('name', e.target.value)}
+ placeholder="이름을 입력해 주세요."
+ disabled
+ />
+
+
+ handleInputValueChange('name', e.target.value)}
+ placeholder="이름을 입력해 주세요."
+ disabled
+ />
+
+
+
+
+
+
+
+ handleInputValueChange('studentId', e.target.value)}
+ placeholder="학번을 입력해 주세요."
+ disabled
+ />
+
+
+ handleInputValueChange('studentId', e.target.value)}
+ placeholder="학번을 입력해 주세요."
+ disabled
+ />
+
+
+
+
+
+
+
+ handleInputValueChange('email', e.target.value)}
+ placeholder="이메일을 입력해 주세요."
+ disabled
+ />
+
+
+ handleInputValueChange('email', e.target.value)}
+ placeholder="이메일을 입력해 주세요."
+ disabled
+ />
+
+
+
+
+
+
+
+
+ {
+ setFormData((prev) => ({ ...prev, major: nextValue }))
+ }}
+ isInvalid={Boolean(errors.major)}
+ />
+
+
+ {
+ setFormData((prev) => ({ ...prev, major: nextValue }))
+ }}
+ isInvalid={Boolean(errors.major)}
+ />
+
+
+
+
+
+
+
+ handleInputValueChange('phone', e.target.value)}
+ placeholder="전화번호를 입력해 주세요."
+ state={errors.phone ? 'error' : 'available'}
+ />
+
+
+ handleInputValueChange('phone', e.target.value)}
+ placeholder="전화번호를 입력해 주세요."
+ state={errors.phone ? 'error' : 'available'}
+ />
+
+
+
+ >
+ ) : null}
+
+ {currentStep === 1 ? (
+ <>
+
+
+
+ {TEAM_OPTIONS.map((team) => {
+ const selected = formData.team === team.id
+
+ return (
+
+
+ handleTeamChange(team.id)}
+ >
+ {team.label}
+
+
+
+ handleTeamChange(team.id)}
+ >
+ {team.label}
+
+
+
+ )
+ })}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {formData.files.map((file, index) => (
+ <>
+
+ handleRemoveFile(index)}
+ />
+
+
+ handleRemoveFile(index)}
+ />
+
+ >
+ ))}
+
+
+
+
document.getElementById('portfolio')?.click()}
+ className="w-full"
+ />
+
+ >
+ ) : null}
+
+ {currentStep === 2 ? (
+ <>
+
+
모집 일정
+
+
+
+
서류 지원 기간
+
2026. 2. 12.(목) - 2026. 3. 14.(토) 23:59:59
+
+
+
서류 결과 발표
+
~ 2026. 3. 15.(일)
+
+
+
면접 진행 기간
+
2026. 3. 16.(월) - 2026. 3. 20.(금)
+
+ ※ 지원자 및 면접관 일정에 따라 마감 전 면접이 가능할 수 있습니다.
+
+
+
+
최종 결과 발표
+
~ 2026. 3. 21.(토)
+
+
+
+
+
+
+
면접 안내
+
+
원칙적으로 대면 면접을 진행하며, 부득이한 경우 비대면으로 조정될 수 있습니다.
+
+ 면접은 인하대학교 내부 장소에서 진행됩니다.
+
+
+
+
+
+
+
활동 안내
+
+
운영진으로 활동 시, 매주 1회 정기 운영진 회의에 필수 참석해야 합니다.
+
※ 일정은 3월 내로 공지 드립니다.
+
+
+
+
*
+
전체 일정을 확인하였습니다.
+
+
+
+
+
+
+
+
+
+ {errors.scheduleCheck ? (
+
+ ※ 미확인 시 지원서 제출이 불가합니다.
+
+ ) : null}
+ >
+ ) : null}
+
+ {currentStep === 3 ? (
+ <>
+
+
+
+
*
+
개인정보 처리방침에 동의합니다.
+
+
+
+
+
+
+
+
+
+ {errors.agreementCheck ? (
+
+ ※ 미동의 시 지원서 제출이 불가합니다.
+
+ ) : null}
+ >
+ ) : null}
+
+
+ {currentStep > 0 ? (
+
+ 이전
+
+ ) : null}
+
+
+ {currentStep === 3 ? '제출하기' : '다음'}
+
+
+
+
+
+ {currentStep > 0 ? (
+
+ 이전
+
+ ) : (
+
+ )}
+
+
+ {currentStep === 3 ? '제출하기' : '다음'}
+
+
+
+
+
+ )
+}
diff --git a/src/app/recruit/layout.js b/src/app/recruit/layout.js
deleted file mode 100644
index f93faa9..0000000
--- a/src/app/recruit/layout.js
+++ /dev/null
@@ -1,15 +0,0 @@
-import { Suspense } from "react";
-import Loader from '@/components/ui/common/Loader.jsx';
-
-export const metadata = {
- title: "Recruit",
- description: "Recruitment management and participation platform",
-};
-
-export default function RecruitLayout({ children }) {
- return (
- }>
- {children}
-
- );
-}
\ No newline at end of file
diff --git a/src/app/recruit/layout.tsx b/src/app/recruit/layout.tsx
new file mode 100644
index 0000000..4939796
--- /dev/null
+++ b/src/app/recruit/layout.tsx
@@ -0,0 +1,11 @@
+import { Suspense } from 'react'
+import Loader from '@/components/ui/common/Loader'
+
+export const metadata = {
+ title: 'Recruit',
+ description: 'Recruitment management and participation platform'
+}
+
+export default function RecruitLayout({ children }) {
+ return }>{children}
+}
diff --git a/src/app/recruit/member/completed/page.tsx b/src/app/recruit/member/completed/page.tsx
new file mode 100644
index 0000000..9e79a44
--- /dev/null
+++ b/src/app/recruit/member/completed/page.tsx
@@ -0,0 +1,188 @@
+'use client'
+
+import Link from 'next/link'
+import { useSearchParams } from 'next/navigation'
+import { GdgLogo } from '@/components/ui/design-system'
+import { cn } from '@/utils/cn'
+
+function Section({ title, children }: { title: string; children: React.ReactNode }) {
+ return (
+
+ )
+}
+
+function Card({ children, className }: { children: React.ReactNode; className?: string }) {
+ return (
+
+ {children}
+
+ )
+}
+
+function Bullet({ children }: { children: React.ReactNode }) {
+ return (
+
+ )
+}
+
+function CoreMemberPromoSection() {
+ return (
+
+
+
+
+
+
+
+
+
+ {'잠깐!\n혹시 코어 멤버에 대해 들어보셨나요?'}
+
+
+
+ {`코어 멤버는 GDGoC INHA 활동의 꽃,
+동아리를 직접 기획하고 운영하는 핵심 운영진입니다.
+
+행사를 만들고, 팀을 이끌고,
+동아리의 방향을 함께 고민합니다.
+
+단순한 참여를 넘어 직접 만들어보고 싶다면,
+
+.
+.
+.`}
+
+
+
+
+ 코어 멤버 모집 바로가기
+
+
+
+ )
+}
+
+export default function RecruitSubmit() {
+ const searchParams = useSearchParams()
+ const isFromRecruit = searchParams.get('from') === 'recruit'
+
+ return (
+
+
+
+
+
+ {isFromRecruit ? 'GDGoC INHA 지원 완료' : 'GDGoC INHA 일정 안내'}
+
+
+
+
+ {isFromRecruit && (
+
+ 지원해주셔서 감사합니다.
+
+ 아래 안내 사항을 확인해 주세요.
+
+
+ )}
+
+
+
+
+ 집중 모집 기간
+ 2/12 ~ 3/15
+
+
+
+
+ 이후 상시 모집으로 전환됩니다.
+
+
+
+
+
+
+ 회비
+ 20,000원
+
+
+ 입금 계좌
+ 신한 110-333-968130 (예금주: 백승엽)
+
+
+ 회비 납부 확인 후, GDGoC INHA의 멤버로서 모든 활동들에 대한 참가 권한을 얻게 됩니다.
+
+
+ 프로젝트와 스터디 등 일부 활동은 운영 소요에 따라 추가금이 산정될 수 있습니다.
+
+ 모든 회비는 커뮤니티 운영비로 투명하게 사용, 처리됩니다.
+
+
+ 회비 입금이 완료되어야 최종 등록이 확정됩니다.
+
+
+
+
+
+ 동아리 단체 채팅방은 매주 금요일에 일괄 초대됩니다.
+
+
+
+
+
+
+ 카카오톡 오픈채팅
+
+
+ https://open.kakao.com/o/s3fxV67h
+
+
+
+ 인스타그램 DM
+
+ @gdgoc.inha
+
+
+
+
+
+
+
+
+ )
+}
diff --git a/src/app/recruit/member/layout.tsx b/src/app/recruit/member/layout.tsx
new file mode 100644
index 0000000..3cce3d0
--- /dev/null
+++ b/src/app/recruit/member/layout.tsx
@@ -0,0 +1,11 @@
+import { type ReactNode } from 'react'
+import { type Metadata } from 'next'
+
+export const metadata: Metadata = {
+ title: '멤버 모집',
+ description: 'GDGoC INHA 2026-1 신입 멤버를 모집합니다.'
+}
+
+export default function RecruitMemberLayout({ children }: { children: ReactNode }) {
+ return <>{children}>
+}
diff --git a/src/app/recruit/member/page.tsx b/src/app/recruit/member/page.tsx
new file mode 100644
index 0000000..dc1cb42
--- /dev/null
+++ b/src/app/recruit/member/page.tsx
@@ -0,0 +1,932 @@
+'use client'
+
+import { type FormEvent, useMemo, useRef, useState } from 'react'
+import { useRouter, useSearchParams } from 'next/navigation'
+import { Select, SelectItem } from '@nextui-org/react'
+import axios from 'axios'
+
+import Loader from '@/components/ui/common/Loader'
+import { PrivacyPolicyNotice } from '@/components/ui/common/PrivacyPolicyNotice'
+import {
+ GdgButton,
+ GdgCheckbox,
+ GdgDropdown,
+ GdgFieldContainer,
+ type GdgFieldStatus,
+ GdgFileCard,
+ GdgInputField,
+ GdgLogo,
+ GdgMajorDropdown,
+ GdgTextarea,
+ GdgUploadButton
+} from '@/components/ui/design-system'
+import { interestOptions } from '@/constant/interestOptions'
+import { wishOptions } from '@/constant/wishOptions'
+import { formatRecruitData } from '@/utils/formatRecruitData'
+import { formatDateInput } from '@/utils/date'
+import {
+ formatPhoneNumberInput,
+ isPhoneNumberFormatValid,
+ toPhoneDigits
+} from '@/utils/phoneNumber'
+import { useAuth } from '@/hooks/useAuth'
+
+type RecruitFormState = {
+ privacyAgreement: boolean
+ name: string
+ gender: string
+ birth: string
+ major: string
+ enrolledClassification: string
+ grade: string
+ studentId: string
+ phoneNumber: string
+ nationality: string
+ emailLocal: string
+ emailDomain: string
+ gdgInterest: string[]
+ gdgWish: string[]
+ gdgPeriod: string[]
+ gdgRoute: string
+ gdgRouteEtc: string
+ gdgExpect: string[]
+ gdgFeedback: string
+ isPayed: boolean
+ proofFile: File | null
+}
+
+type DuplicateCheckStatus = 'idle' | 'checking' | 'available' | 'duplicate' | 'unverified' | 'error'
+
+interface DuplicateCheckState {
+ status: DuplicateCheckStatus
+ message?: string
+ checkedValue?: string
+ verifiedValue?: string
+}
+
+const initialFormState: RecruitFormState = {
+ privacyAgreement: false,
+ name: '',
+ gender: '',
+ birth: '',
+ major: '',
+ enrolledClassification: '',
+ grade: '1학년',
+ studentId: '',
+ phoneNumber: '',
+ nationality: '대한민국',
+ emailLocal: '',
+ emailDomain: 'inha.edu',
+ gdgInterest: [],
+ gdgWish: [],
+ gdgPeriod: [],
+ gdgRoute: '기타',
+ gdgRouteEtc: '',
+ gdgExpect: [],
+ gdgFeedback: '',
+ isPayed: false,
+ proofFile: null
+}
+
+const enrollmentOptions = ['재학', '부분등록', '휴학', '군휴학', '수료', '졸업']
+const genderOptions = [
+ { id: '남성', label: '남성' },
+ { id: '여성', label: '여성' },
+ { id: '비공개', label: '비공개' }
+]
+
+const recruitMultiSelectClassNames = {
+ trigger:
+ 'h-11 rounded-full bg-black border border-gray-800 px-4 data-[hover=true]:border-gray-900 data-[open=true]:border-white',
+ value: 'text-gray-700 typo-b2 mobile:typo-m-b3',
+ popoverContent:
+ 'bg-gray-100 border border-white/10 rounded-xl shadow-[0_20px_120px_rgba(0,0,0,0.75)] p-3',
+ listboxWrapper: 'max-h-58',
+ listbox: 'p-0 m-0'
+}
+
+const recruitMultiSelectItemClasses = {
+ base: [
+ 'h-9 px-2 rounded-lg flex items-center justify-between',
+ 'text-white typo-b2 mobile:typo-m-b3 font-medium',
+ 'data-[hover=true]:bg-white data-[hover=true]:text-black',
+ 'data-[selected=true]:font-medium',
+ 'data-[hover=true]:[&_[data-slot=selected-icon]]:text-black'
+ ].join(' '),
+ title: 'font-medium'
+}
+
+export default function Recruit() {
+ const router = useRouter()
+ const searchParams = useSearchParams()
+ const { user } = useAuth()
+ const isPreview = searchParams?.get('preview') === '1'
+ const [formData, setFormData] = useState(initialFormState)
+ const [loading, setLoading] = useState(false)
+ const [studentCheckState, setStudentCheckState] = useState({
+ status: 'idle'
+ })
+ const [phoneCheckState, setPhoneCheckState] = useState({ status: 'idle' })
+ const [emailCheckState, setEmailCheckState] = useState({ status: 'idle' })
+ const [isSubmitted, setIsSubmitted] = useState(false)
+ const fileInputRef = useRef(null)
+ const [globalError, setGlobalError] = useState(null)
+
+ const interestDropdownOptions = useMemo(
+ () => interestOptions.map((option) => ({ id: option, label: option })),
+ []
+ )
+ const wishDropdownOptions = useMemo(
+ () => wishOptions.map((option) => ({ id: option, label: option })),
+ []
+ )
+
+ const handleFileChange = (event: React.ChangeEvent) => {
+ const file = event.target.files?.[0]
+ if (file) setFormData((prev) => ({ ...prev, proofFile: file }))
+ }
+
+ const handleFileRemove = () => {
+ setFormData((prev) => ({ ...prev, proofFile: null }))
+ if (fileInputRef.current) fileInputRef.current.value = ''
+ }
+
+ const handleValueChange = (field: keyof RecruitFormState) => (value: string) => {
+ const nextValue =
+ field === 'phoneNumber'
+ ? formatPhoneNumberInput(value)
+ : field === 'birth'
+ ? formatDateInput(value)
+ : value
+ setFormData((prev) => ({ ...prev, [field]: nextValue }))
+
+ if (field === 'studentId') {
+ const trimmed = nextValue.trim()
+ if (studentCheckState.verifiedValue === trimmed && trimmed !== '') {
+ setStudentCheckState((prev) => ({
+ ...prev,
+ status: 'available',
+ message: '※ 가입 가능한 학번입니다.',
+ checkedValue: trimmed
+ }))
+ } else {
+ setStudentCheckState((prev) => ({ status: 'idle', verifiedValue: prev.verifiedValue }))
+ }
+ }
+
+ if (field === 'phoneNumber') {
+ const trimmed = nextValue.trim()
+ if (phoneCheckState.verifiedValue === trimmed && trimmed !== '') {
+ setPhoneCheckState((prev) => ({
+ ...prev,
+ status: 'available',
+ message: '※ 가입 가능한 전화번호입니다.',
+ checkedValue: trimmed
+ }))
+ } else {
+ setPhoneCheckState((prev) => ({ status: 'idle', verifiedValue: prev.verifiedValue }))
+ }
+ }
+
+ if (field === 'emailLocal') {
+ const currentEmail = `${nextValue.trim()}@${formData.emailDomain}`
+ if (emailCheckState.verifiedValue === currentEmail && nextValue.trim() !== '') {
+ setEmailCheckState((prev) => ({
+ ...prev,
+ status: 'available',
+ message: '※ 가입 가능한 이메일입니다.',
+ checkedValue: currentEmail
+ }))
+ } else {
+ setEmailCheckState((prev) => ({ status: 'idle', verifiedValue: prev.verifiedValue }))
+ }
+ }
+ }
+
+ const handleLimitedMultiSelection =
+ (field: 'gdgInterest' | 'gdgWish', label: string) => (rawValue: string) => {
+ const selected = rawValue
+ .split(',')
+ .map((item) => item.trim())
+ .filter(Boolean)
+ if (selected.length > 3) {
+ alert(`${label}는 최대 3개까지 선택할 수 있습니다.`)
+ return
+ }
+ setFormData((prev) => ({ ...prev, [field]: selected }))
+ }
+
+ const handleStudentCheck = async () => {
+ const candidate = formData.studentId.trim()
+ if (!candidate || candidate.length !== 8) return
+ setStudentCheckState({ status: 'checking', checkedValue: candidate })
+ try {
+ const response = await axios.post(
+ `${process.env.NEXT_PUBLIC_BASE_API_URL}/recruit/member/check/student-id`,
+ { studentId: candidate }
+ )
+ const isExists = response.data?.data?.isExists
+ if (isExists) {
+ setStudentCheckState({
+ status: 'duplicate',
+ message: '※ 중복된 학번입니다.',
+ checkedValue: candidate
+ })
+ return
+ }
+ setStudentCheckState({
+ status: 'available',
+ message: '※ 가입 가능한 학번입니다.',
+ checkedValue: candidate,
+ verifiedValue: candidate
+ })
+ } catch {
+ setStudentCheckState({
+ status: 'error',
+ message: '※ 중복 확인 중 오류가 발생했습니다.',
+ checkedValue: candidate
+ })
+ }
+ }
+
+ const handlePhoneCheck = async () => {
+ const candidate = formData.phoneNumber.trim()
+ if (!candidate || !isPhoneNumberFormatValid(candidate)) return
+ const digits = toPhoneDigits(candidate)
+ setPhoneCheckState({ status: 'checking', checkedValue: digits })
+ try {
+ const response = await axios.post(
+ `${process.env.NEXT_PUBLIC_BASE_API_URL}/recruit/member/check/phone-number`,
+ { phoneNumber: digits }
+ )
+ const isExists = response.data?.data?.isExists
+ if (isExists) {
+ setPhoneCheckState({
+ status: 'duplicate',
+ message: '※ 중복된 전화번호입니다.',
+ checkedValue: candidate
+ })
+ return
+ }
+ setPhoneCheckState({
+ status: 'available',
+ message: '※ 가입 가능한 전화번호입니다.',
+ checkedValue: candidate,
+ verifiedValue: candidate
+ })
+ } catch {
+ setPhoneCheckState({
+ status: 'error',
+ message: '※ 중복 확인 중 오류가 발생했습니다.',
+ checkedValue: candidate
+ })
+ }
+ }
+
+ const handleEmailCheck = async () => {
+ const emailLocal = formData.emailLocal.trim()
+ if (!emailLocal) return
+ const fullEmail = `${emailLocal}@${formData.emailDomain}`
+ setEmailCheckState({ status: 'checking', checkedValue: fullEmail })
+ try {
+ const response = await axios.post(
+ `${process.env.NEXT_PUBLIC_BASE_API_URL}/recruit/member/check/email`,
+ { email: fullEmail }
+ )
+ const isExists = response.data?.data?.isExists
+ if (isExists) {
+ setEmailCheckState({
+ status: 'duplicate',
+ message: '※ 중복된 이메일입니다.',
+ checkedValue: fullEmail
+ })
+ return
+ }
+ setEmailCheckState({
+ status: 'available',
+ message: '※ 가입 가능한 이메일입니다.',
+ checkedValue: fullEmail,
+ verifiedValue: fullEmail
+ })
+ } catch {
+ setEmailCheckState({
+ status: 'error',
+ message: '※ 중복 확인 중 오류가 발생했습니다.',
+ checkedValue: fullEmail
+ })
+ }
+ }
+
+ const isFormValid = useMemo(() => {
+ const required = ['name', 'gender', 'birth', 'major', 'enrolledClassification']
+ const hasStrings = required.every(
+ (f) => String(formData[f as keyof RecruitFormState]).trim() !== ''
+ )
+ const isChecksPassed =
+ formData.studentId.trim() === studentCheckState.verifiedValue &&
+ formData.phoneNumber.trim() === phoneCheckState.verifiedValue &&
+ `${formData.emailLocal.trim()}@${formData.emailDomain}` === emailCheckState.verifiedValue
+ const isProofPassed =
+ formData.enrolledClassification !== '군휴학' || formData.proofFile !== null
+ return (
+ hasStrings &&
+ formData.gdgInterest.length > 0 &&
+ formData.gdgWish.length > 0 &&
+ isChecksPassed &&
+ isProofPassed &&
+ formData.isPayed
+ )
+ }, [formData, phoneCheckState, studentCheckState, emailCheckState])
+
+ const handleSubmit = async (event: FormEvent) => {
+ event.preventDefault()
+ setIsSubmitted(true)
+ if (isPreview || !isFormValid) return
+ try {
+ setLoading(true)
+ const buildRecruitMap = () => {
+ const map = new Map>()
+ map.set(1, { isAgree: formData.isPayed })
+ map.set(2, {
+ name: formData.name,
+ studentId: formData.studentId,
+ enrolledClassification: formData.enrolledClassification
+ })
+ map.set(3, {
+ grade: formData.grade,
+ phoneNumber: toPhoneDigits(formData.phoneNumber),
+ nationality: formData.nationality
+ })
+ map.set(4, {
+ gender: formData.gender,
+ birth: formData.birth,
+ email: `${formData.emailLocal.trim()}@${formData.emailDomain}`
+ })
+ map.set(5, { major: formData.major })
+ map.set(10, { gdgFeedback: formData.gdgFeedback })
+ map.set(11, { isPayed: formData.isPayed })
+ return map
+ }
+ const formattedData = formatRecruitData(buildRecruitMap())
+ if (formData.enrolledClassification === '군휴학' && formData.proofFile) {
+ const fd = new FormData()
+ fd.append(
+ 'request',
+ new Blob([JSON.stringify(formattedData)], { type: 'application/json' })
+ )
+ fd.append('file', formData.proofFile)
+ await axios.post(`${process.env.NEXT_PUBLIC_BASE_API_URL}/recruit/member/apply`, fd, {
+ headers: { 'Content-Type': 'multipart/form-data' }
+ })
+ } else {
+ await axios.post(
+ `${process.env.NEXT_PUBLIC_BASE_API_URL}/recruit/member/apply`,
+ formattedData
+ )
+ }
+ router.push('/recruit/member/completed?from=recruit')
+ } catch (error: any) {
+ setGlobalError(error.response?.data?.message || '지원서 제출 중 오류가 발생했습니다.')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const studentStatus: GdgFieldStatus | undefined =
+ formData.studentId.trim() === studentCheckState.verifiedValue
+ ? 'success'
+ : studentCheckState.status === 'error' || studentCheckState.status === 'duplicate'
+ ? 'error'
+ : undefined
+ const studentStatusMessage =
+ formData.studentId.trim() === studentCheckState.verifiedValue
+ ? '※ 가입 가능한 학번입니다.'
+ : studentCheckState.message
+
+ const phoneStatus: GdgFieldStatus | undefined =
+ formData.phoneNumber.trim() === phoneCheckState.verifiedValue
+ ? 'success'
+ : phoneCheckState.status === 'error' || phoneCheckState.status === 'duplicate'
+ ? 'error'
+ : undefined
+ const phoneStatusMessage =
+ formData.phoneNumber.trim() === phoneCheckState.verifiedValue
+ ? '※ 가입 가능한 전화번호입니다.'
+ : phoneCheckState.message
+
+ const emailStatus: GdgFieldStatus | undefined =
+ `${formData.emailLocal.trim()}@${formData.emailDomain}` === emailCheckState.verifiedValue
+ ? 'success'
+ : emailCheckState.status === 'error' || emailCheckState.status === 'duplicate'
+ ? 'error'
+ : undefined
+ const emailStatusMessage =
+ `${formData.emailLocal.trim()}@${formData.emailDomain}` === emailCheckState.verifiedValue
+ ? '※ 가입 가능한 이메일입니다.'
+ : emailCheckState.message
+
+ const enrollmentStatus: GdgFieldStatus | undefined =
+ isSubmitted && !formData.enrolledClassification ? 'error' : undefined
+ const enrollmentStatusMessage =
+ isSubmitted && !formData.enrolledClassification ? '※ 필수 선택 사항입니다.' : undefined
+
+ const isStudentCheckDisabled =
+ !formData.studentId.trim() ||
+ formData.studentId.trim().length !== 8 ||
+ studentCheckState.status === 'checking' ||
+ formData.studentId.trim() === studentCheckState.verifiedValue
+ const isPhoneCheckDisabled =
+ !formData.phoneNumber.trim() ||
+ !isPhoneNumberFormatValid(formData.phoneNumber) ||
+ phoneCheckState.status === 'checking' ||
+ formData.phoneNumber.trim() === phoneCheckState.verifiedValue
+ const isEmailCheckDisabled =
+ !formData.emailLocal.trim() ||
+ emailCheckState.status === 'checking' ||
+ `${formData.emailLocal.trim()}@${formData.emailDomain}` === emailCheckState.verifiedValue
+
+ return (
+ <>
+
+
+
+
+
+
+
+
기본 정보
+
+
+ {/* 이름 & 성별 */}
+
+
+
+ handleValueChange('name')(e.target.value)}
+ placeholder="이름을 입력해 주세요."
+ width="twoThirds"
+ />
+
+
+
+
+ handleValueChange('name')(e.target.value)}
+ placeholder="이름을 입력해 주세요."
+ width="twoThirds"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* 생년월일 */}
+
+
+ handleValueChange('birth')(e.target.value)}
+ placeholder="생년월일을 입력해 주세요. (YYYY.MM.DD)"
+ width="full"
+ />
+
+
+ handleValueChange('birth')(e.target.value)}
+ placeholder="생년월일을 입력해 주세요. (YYYY.MM.DD)"
+ width="full"
+ />
+
+
+
+ {/* 주전공 */}
+
+
+
+
+
+
+
+
+
+ {/* 재학 상태 */}
+
+
+ {enrollmentOptions.map((option) => (
+
+
+ handleValueChange('enrolledClassification')(option)}
+ >
+ {option}
+
+
+
+ handleValueChange('enrolledClassification')(option)}
+ >
+ {option}
+
+
+
+ ))}
+
+
+
+ {/* 증빙 서류 */}
+ {formData.enrolledClassification === '군휴학' && (
+
+
+ {!formData.proofFile ? (
+
+ fileInputRef.current?.click()}
+ />
+
+ ) : (
+
+
+
+ )}
+
+ )}
+
+
+
+
+ 중복 확인
+
+
+
+
+
+ 중복 확인
+
+
+ >
+ }
+ >
+ <>
+
+ handleValueChange('studentId')(e.target.value)}
+ placeholder="학번을 입력해 주세요."
+ width="twoThirds"
+ state={studentStatus === 'error' ? 'error' : 'available'}
+ />
+
+
+
+ handleValueChange('studentId')(e.target.value)}
+ placeholder="학번을 입력해 주세요."
+ width="twoThirds"
+ state={studentStatus === 'error' ? 'error' : 'available'}
+ />
+
+ >
+
+
+
+
+
+ 중복 확인
+
+
+
+
+
+ 중복 확인
+
+
+ >
+ }
+ >
+ <>
+
+ handleValueChange('phoneNumber')(e.target.value)}
+ placeholder="전화번호를 입력해 주세요."
+ width="twoThirds"
+ state={phoneStatus === 'error' ? 'error' : 'available'}
+ />
+
+
+
+ handleValueChange('phoneNumber')(e.target.value)}
+ placeholder="전화번호를 입력해 주세요."
+ width="twoThirds"
+ state={phoneStatus === 'error' ? 'error' : 'available'}
+ />
+
+ >
+
+
+
+
+
+ 중복 확인
+
+
+
+
+
+ 중복 확인
+
+
+ >
+ }
+ >
+ <>
+
+ handleValueChange('emailLocal')(e.target.value)}
+ placeholder="이메일 아이디를 입력해 주세요."
+ width="twoThirds"
+ state={emailStatus === 'error' ? 'error' : 'available'}
+ endAdornment={@inha.edu }
+ />
+
+
+
+ handleValueChange('emailLocal')(e.target.value)}
+ placeholder="이메일 아이디"
+ width="twoThirds"
+ state={emailStatus === 'error' ? 'error' : 'available'}
+ endAdornment={@inha.edu }
+ />
+
+ >
+
+
+
+
+
+
흥미 및 활동 성향
+
+
+ handleLimitedMultiSelection('gdgInterest', '관심 분야')(e.target.value)
+ }
+ placeholder="최대 3개까지 선택 가능합니다."
+ classNames={recruitMultiSelectClassNames}
+ listboxProps={{ itemClasses: recruitMultiSelectItemClasses }}
+ renderValue={(items) => (
+
+ {items.map((i) => String(i.textValue ?? '')).join(', ')}
+
+ )}
+ >
+ {interestDropdownOptions.map((o) => (
+ {o.label}
+ ))}
+
+
+
+
+ handleLimitedMultiSelection('gdgWish', '하고 싶은 활동')(e.target.value)
+ }
+ placeholder="최대 3개까지 선택 가능합니다."
+ classNames={recruitMultiSelectClassNames}
+ listboxProps={{ itemClasses: recruitMultiSelectItemClasses }}
+ renderValue={(items) => (
+
+ {items.map((i) => String(i.textValue ?? '')).join(', ')}
+
+ )}
+ >
+ {wishDropdownOptions.map((o) => (
+ {o.label}
+ ))}
+
+
+
+
+ setFormData((p) => ({ ...p, gdgFeedback: e.target.value.slice(0, 100) }))
+ }
+ maxLength={100}
+ placeholder="내용을 입력해 주세요."
+ fullWidth
+ />
+
+
+
+
+
+
+ * 개인정보 처리방침에 동의합니다.
+
+
setFormData((p) => ({ ...p, isPayed: c }))}
+ />
+
+
+
+
+
+
+
+
+
+ 제출하기
+
+
+
+
+ 제출하기
+
+
+
+
+
+
+ >
+ )
+}
diff --git a/src/app/recruit/page.jsx b/src/app/recruit/page.jsx
deleted file mode 100644
index 9d3b8bd..0000000
--- a/src/app/recruit/page.jsx
+++ /dev/null
@@ -1,134 +0,0 @@
-"use client";
-
-import { useState, useCallback } from "react";
-import { useRouter } from 'next/navigation';
-import { Button } from '@nextui-org/react';
-import axios from 'axios';
-
-import Loader from '@/components/ui/common/Loader.jsx';
-import VerticalProgressBar from '@/components/ui/progressbar/VerticalProgressBar.jsx';
-import HorizontalProgressBar from '@/components/ui/progressbar/HorizontalProgressBar.jsx';
-
-import Recruit1 from '@/components/recruit/screen/Recruit1';
-import Recruit2 from '@/components/recruit/screen/Recruit2';
-import Recruit3 from '@/components/recruit/screen/Recruit3';
-import Recruit4 from '@/components/recruit/screen/Recruit4';
-import Recruit5 from '@/components/recruit/screen/Recruit5';
-import Recruit6 from '@/components/recruit/screen/Recruit6';
-import Recruit7 from '@/components/recruit/screen/Recruit7';
-import Recruit8 from '@/components/recruit/screen/Recruit8';
-import Recruit9 from '@/components/recruit/screen/Recruit9';
-import Recruit10 from '@/components/recruit/screen/Recruit10';
-import Recruit11 from '@/components/recruit/screen/Recruit11';
-
-import { formatRecruitData } from '@/utils/formatRecruitData.js';
-
-export default function Recruit() {
- const router = useRouter();
-
- const [mainRecruitData, setMainRecruitData] = useState(new Map());
- const [step, setStep] = useState(1);
- const [mount, setMount] = useState(1);
- const [checked, setChecked] = useState(false);
- const [loading, setLoading] = useState(false);
-
- const handleNext = async () => {
- if (!checked) {
- alert("신청서의 공란을 모두 기입해 주세요.");
- return;
- }
- if (step >= 11) {
- const confirmation = window.confirm("정말 제출하시겠습니까?");
- if (confirmation) {
- const formattedData = formatRecruitData(mainRecruitData);
- try {
- setLoading(true)
- const response = await axios.post(process.env.NEXT_PUBLIC_BASE_API_URL + "/apply", formattedData);
- router.push("/recruit/submit");
- } catch (error) {
- if (error.response && error.response.status === 500) {
- alert("이미 가입된 회원입니다.");
- } else {
- alert("제출 중 오류가 발생했습니다. 다시 시도해 주세요.");
- }
- setLoading(false)
- }
- }
- } else {
- setStep((prev) => prev + 1);
- setMount((prev) => Math.max(prev, step + 1));
- }
- };
-
- const handlePrevious = () => {
- setStep((prev) => Math.max(prev - 1, 1));
- };
-
- const updateRecruitData = useCallback((step, data) => {
- setMainRecruitData((prev) => new Map(prev).set(step, data));
- }, []);
-
- return (
-
-
-
-
-
- G
- D
- G
- o
- C INHA 25-2
- 멤버에 지원해보세요
-
-
-
-
-
-
개인정보 수집
-
필수 인적 사항
-
단과대학 | 전공
-
지원 정보
-
설문조사
-
회비 안내
-
-
-
-
- {mount >= 0 && }
- {mount >= 1 && }
- {mount >= 2 && }
- {mount >= 3 && }
- {mount >= 4 && }
- {mount >= 5 && }
- {mount >= 6 && }
- {mount >= 7 && }
- {mount >= 8 && }
- {mount >= 9 && }
- {mount >= 10 && }
-
-
-
- {step > 1 && (
-
- 이전
-
- )}
-
- {step < 11 ? '다음' : '완료'}
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/recruit/page.tsx b/src/app/recruit/page.tsx
new file mode 100644
index 0000000..e16e717
--- /dev/null
+++ b/src/app/recruit/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation'
+
+export default function RecruitRedirect() {
+ redirect('/recruit/member')
+}
diff --git a/src/app/recruit/submit/page.jsx b/src/app/recruit/submit/page.jsx
deleted file mode 100644
index a9bc411..0000000
--- a/src/app/recruit/submit/page.jsx
+++ /dev/null
@@ -1,51 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { useRouter } from 'next/navigation';
-import { Button } from "@nextui-org/react";
-
-export default function RecruitSubmit() {
- const router = useRouter()
- const [checkMotion, setCheckMotion] = useState(false);
-
- useEffect(() => {
- const timer = setTimeout(() => {
- setCheckMotion(true);
- }, 50);
-
- return () => clearTimeout(timer);
- }, []);
-
- return (
-
-
-
- ✓
-
-
-
25-2 멤버 가입이 완료되었습니다.
-
-
신규 멤버 초대는 매주 월요일 오후 3시에 일괄 진행됩니다.
-
-
회비 납부 확인 후,
- G
- D
- G
- o
- C INHA
-
-
채팅방에 초대해 드리겠습니다.
-
-
-
router.push('/')}>메인으로 이동하기
-
- );
-}
\ No newline at end of file
diff --git a/src/app/robots.js b/src/app/robots.ts
similarity index 97%
rename from src/app/robots.js
rename to src/app/robots.ts
index baa8391..1fd1a8c 100644
--- a/src/app/robots.js
+++ b/src/app/robots.ts
@@ -25,7 +25,7 @@ export default function robots() {
'/hooks/',
'/mock/',
'/utils/',
- '/recruit/'
+ '/recruit/member/'
],
crawlDelay: 5,
},
diff --git a/src/app/signup/layout.tsx b/src/app/signup/layout.tsx
new file mode 100644
index 0000000..144dff3
--- /dev/null
+++ b/src/app/signup/layout.tsx
@@ -0,0 +1,15 @@
+import type { Metadata } from 'next'
+import type { ReactNode } from 'react'
+
+export const metadata: Metadata = {
+ title: '회원가입',
+ description: 'GDGoC INHA 멤버 가입을 완료하세요.'
+}
+
+export default function SignupLayout({ children }: { children: ReactNode }) {
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/app/signup/page.tsx b/src/app/signup/page.tsx
new file mode 100644
index 0000000..c44468b
--- /dev/null
+++ b/src/app/signup/page.tsx
@@ -0,0 +1,448 @@
+'use client'
+
+import { type FormEvent, useCallback, useEffect, useMemo, useState } from 'react'
+import axios from 'axios'
+import { useRouter, useSearchParams } from 'next/navigation'
+
+import { GdgMajorDropdown } from '@/components/ui/design-system/GdgMajorDropdown'
+import {
+ GdgButton,
+ GdgCheckbox,
+ GdgFieldContainer,
+ GdgLogo,
+ GdgInputField,
+ type GdgFieldStatus
+} from '@/components/ui/design-system'
+import Loader from '@/components/ui/common/Loader'
+import { PrivacyPolicyNotice } from '@/components/ui/common/PrivacyPolicyNotice'
+import { useAuth } from '@/hooks/useAuth'
+import {
+ checkPhoneNumberDuplicated,
+ checkStudentIdDuplicated,
+ type DuplicateCheckResponseBody,
+ type SignupResponseBody,
+ signupWithProfile
+} from '@/services/auth/authClient'
+import { PENDING_SIGNUP_STORAGE_KEY, type PendingSignupPayload } from '@/constant/auth'
+import { unwrapApiResponse } from '@/utils/api/unwrap'
+import {
+ formatPhoneNumberInput,
+ isPhoneNumberFormatValid,
+ toPhoneDigits
+} from '@/utils/phoneNumber'
+
+const DEFAULT_FALLBACK_ROUTE = '/'
+const STUDENT_ID_PATTERN = /^12\d{6}$/
+
+const getSafeNextUrl = (raw: string | null): string => {
+ if (!raw) return DEFAULT_FALLBACK_ROUTE
+
+ try {
+ const decoded = decodeURIComponent(raw)
+ return decoded.startsWith('/') ? decoded : DEFAULT_FALLBACK_ROUTE
+ } catch {
+ return DEFAULT_FALLBACK_ROUTE
+ }
+}
+
+interface FieldErrors {
+ name?: string
+ studentId?: string
+ phoneNumber?: string
+ major?: string
+}
+
+type DuplicateCheckStatus = 'idle' | 'checking' | 'available' | 'duplicate' | 'unverified' | 'error'
+
+interface DuplicateCheckState {
+ status: DuplicateCheckStatus
+ message?: string
+ checkedValue?: string
+ verifiedValue?: string
+}
+
+export default function SignupPage() {
+ const router = useRouter()
+ const searchParams = useSearchParams()
+ const { setUser } = useAuth()
+ const isPreview = searchParams?.get('preview') === '1'
+
+ const [pendingInfo, setPendingInfo] = useState(null)
+ const [initializing, setInitializing] = useState(true)
+ const [name, setName] = useState('')
+ const [studentId, setStudentId] = useState('')
+ const [phoneNumber, setPhoneNumber] = useState('')
+ const [major, setMajor] = useState('')
+ const [isPrivacyAgreed, setIsPrivacyAgreed] = useState(false)
+ const [fieldErrors, setFieldErrors] = useState({})
+ const [globalError, setGlobalError] = useState(null)
+ const [loading, setLoading] = useState(false)
+ const [studentCheckState, setStudentCheckState] = useState({
+ status: 'idle'
+ })
+ const [phoneCheckState, setPhoneCheckState] = useState({
+ status: 'idle'
+ })
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return
+
+ const raw = sessionStorage.getItem(PENDING_SIGNUP_STORAGE_KEY)
+ if (!raw) {
+ if (isPreview) {
+ const previewPayload: PendingSignupPayload = {
+ oauthSubject: 'preview',
+ email: 'preview@gdgoc.dev',
+ name: '홍길동',
+ next: DEFAULT_FALLBACK_ROUTE
+ }
+ setPendingInfo(previewPayload)
+ setName(previewPayload.name)
+ setInitializing(false)
+ return
+ }
+ router.replace('/login')
+ setInitializing(false)
+ return
+ }
+
+ try {
+ const parsed = JSON.parse(raw) as PendingSignupPayload
+ setPendingInfo(parsed)
+ setName(parsed.name ?? '')
+ } catch {
+ if (isPreview) {
+ const previewPayload: PendingSignupPayload = {
+ oauthSubject: 'preview',
+ email: 'preview@gdgoc.dev',
+ name: '홍길동',
+ next: DEFAULT_FALLBACK_ROUTE
+ }
+ setPendingInfo(previewPayload)
+ setName(previewPayload.name)
+ } else {
+ router.replace('/login')
+ }
+ } finally {
+ setInitializing(false)
+ }
+ }, [router, isPreview])
+
+ const requestedNext = pendingInfo?.next ?? searchParams?.get('next') ?? null
+ const nextUrl = useMemo(() => getSafeNextUrl(requestedNext), [requestedNext])
+
+ const phoneDigits = toPhoneDigits(phoneNumber)
+
+ const handleStudentIdChange = useCallback(
+ (value: string) => {
+ const nextValue = value.replace(/\D/g, '').slice(0, 8)
+ setStudentId(nextValue)
+
+ if (studentCheckState.verifiedValue === nextValue && nextValue !== '') {
+ setStudentCheckState((prev) => ({
+ ...prev,
+ status: 'available',
+ message: '※ 가입 가능한 학번입니다.',
+ checkedValue: nextValue
+ }))
+ } else {
+ setStudentCheckState((prev) => ({
+ status: 'idle',
+ verifiedValue: prev.verifiedValue
+ }))
+ }
+ },
+ [studentCheckState.verifiedValue]
+ )
+
+ const handlePhoneNumberChange = useCallback(
+ (value: string) => {
+ const nextValue = formatPhoneNumberInput(value)
+ setPhoneNumber(nextValue)
+
+ if (phoneCheckState.verifiedValue === nextValue && nextValue !== '') {
+ setPhoneCheckState((prev) => ({
+ ...prev,
+ status: 'available',
+ message: '※ 가입 가능한 전화번호입니다.',
+ checkedValue: nextValue
+ }))
+ } else {
+ setPhoneCheckState((prev) => ({
+ status: 'idle',
+ verifiedValue: prev.verifiedValue
+ }))
+ }
+ },
+ [phoneCheckState.verifiedValue]
+ )
+
+ const handleStudentCheck = useCallback(async () => {
+ const candidate = studentId.trim()
+ if (!candidate || !STUDENT_ID_PATTERN.test(candidate)) return
+
+ setStudentCheckState({ status: 'checking', checkedValue: candidate })
+
+ try {
+ const response = await checkStudentIdDuplicated(candidate)
+ const data = unwrapApiResponse(response.data)
+ if (data?.isExists) {
+ setStudentCheckState({
+ status: 'duplicate',
+ message: '※ 중복된 학번입니다.',
+ checkedValue: candidate
+ })
+ return
+ }
+
+ setStudentCheckState({
+ status: 'available',
+ message: '※ 가입 가능한 학번입니다.',
+ checkedValue: candidate,
+ verifiedValue: candidate
+ })
+ } catch {
+ setStudentCheckState({
+ status: 'error',
+ message: '※ 중복 확인 중 오류가 발생했습니다.',
+ checkedValue: candidate
+ })
+ }
+ }, [studentId])
+
+ const handlePhoneCheck = useCallback(async () => {
+ const candidate = phoneNumber.trim()
+ if (!candidate || !isPhoneNumberFormatValid(candidate)) return
+
+ const digits = toPhoneDigits(candidate)
+ setPhoneCheckState({ status: 'checking', checkedValue: digits })
+
+ try {
+ const response = await checkPhoneNumberDuplicated(digits)
+ const data = unwrapApiResponse(response.data)
+ if (data?.isExists) {
+ setPhoneCheckState({
+ status: 'duplicate',
+ message: '※ 중복된 전화번호입니다.',
+ checkedValue: candidate
+ })
+ return
+ }
+
+ setPhoneCheckState({
+ status: 'available',
+ message: '※ 가입 가능한 전화번호입니다.',
+ checkedValue: candidate,
+ verifiedValue: candidate
+ })
+ } catch {
+ setPhoneCheckState({
+ status: 'error',
+ message: '※ 중복 확인 중 오류가 발생했습니다.',
+ checkedValue: candidate
+ })
+ }
+ }, [phoneNumber])
+
+ const handleSubmit = async (event: FormEvent) => {
+ event.preventDefault()
+ if (isPreview || !pendingInfo) return
+
+ setLoading(true)
+
+ try {
+ const response = await signupWithProfile({
+ oauthSubject: pendingInfo.oauthSubject,
+ email: pendingInfo.email,
+ name: name.trim(),
+ studentId: studentId.trim(),
+ phoneNumber: phoneDigits,
+ major,
+ image: pendingInfo.picture
+ })
+
+ const data = unwrapApiResponse(response.data)
+ if (data?.user && data.accessToken && data.refreshToken) {
+ console.log('[SignupPage] Signup success, saving to storage')
+ setUser(data.user, data.accessToken, data.refreshToken)
+ } else {
+ console.warn('[SignupPage] Signup success but missing data', {
+ hasUser: !!data?.user,
+ hasAt: !!data?.accessToken,
+ hasRt: !!data?.refreshToken
+ })
+ }
+ sessionStorage.removeItem(PENDING_SIGNUP_STORAGE_KEY)
+ router.replace(nextUrl)
+ } catch (error: any) {
+ setGlobalError(error.response?.data?.message || '회원가입 중 오류가 발생했습니다.')
+ } finally {
+ setLoading(false)
+ }
+ }
+
+ const isStudentCheckDisabled =
+ !studentId.trim() ||
+ !STUDENT_ID_PATTERN.test(studentId.trim()) ||
+ studentCheckState.status === 'checking' ||
+ studentId.trim() === studentCheckState.verifiedValue
+
+ const isPhoneCheckDisabled =
+ !phoneNumber.trim() ||
+ !isPhoneNumberFormatValid(phoneNumber) ||
+ phoneCheckState.status === 'checking' ||
+ phoneNumber.trim() === phoneCheckState.verifiedValue
+
+ const isSignupReady =
+ name.trim() !== '' &&
+ major !== '' &&
+ studentId.trim() === studentCheckState.verifiedValue &&
+ phoneNumber.trim() === phoneCheckState.verifiedValue &&
+ isPrivacyAgreed
+
+ const studentStatusMessage = studentId === studentCheckState.verifiedValue ? '※ 가입 가능한 학번입니다.' : studentCheckState.message
+ const studentStatus: GdgFieldStatus | undefined =
+ studentId === studentCheckState.verifiedValue
+ ? 'success'
+ : (studentCheckState.status === 'error' || studentCheckState.status === 'duplicate' ? 'error' : undefined)
+
+ const phoneStatusMessage = phoneNumber === phoneCheckState.verifiedValue ? '※ 가입 가능한 전화번호입니다.' : phoneCheckState.message
+ const phoneStatus: GdgFieldStatus | undefined =
+ phoneNumber === phoneCheckState.verifiedValue
+ ? 'success'
+ : (phoneCheckState.status === 'error' || phoneCheckState.status === 'duplicate' ? 'error' : undefined)
+
+ return (
+ <>
+
+
+ {initializing ? null : pendingInfo ? (
+
+
+
+
+ {/* PC Version */}
+
+
+
+ setName(e.target.value)} state={fieldErrors.name ? 'error' : 'available'} width="full" />
+
+
+
+
+
+
+
+
+ 중복 확인
+ }
+ >
+ <>
+ handleStudentIdChange(e.target.value)} state={studentStatus === 'error' ? 'error' : 'available'} width="twoThirds" />
+ >
+
+
+
+ 중복 확인
+ }
+ >
+ <>
+ handlePhoneNumberChange(e.target.value)} state={phoneStatus === 'error' ? 'error' : 'available'} width="twoThirds" />
+ >
+
+
+
+
+ {/* Mobile Version */}
+
+
+
+ setName(e.target.value)} state={fieldErrors.name ? 'error' : 'available'} width="full" />
+
+
+
+
+
+
+
+
+ 중복 확인
+ }
+ >
+ <>
+ handleStudentIdChange(e.target.value)} state={studentStatus === 'error' ? 'error' : 'available'} width="twoThirds" />
+ >
+
+
+
+ 중복 확인
+ }
+ >
+ <>
+ handlePhoneNumberChange(e.target.value)} state={phoneStatus === 'error' ? 'error' : 'available'} width="twoThirds" />
+ >
+
+
+
+
+
+
+
+
+ * 개인정보 처리방침에 동의합니다.
+
+
+
+
+
+ {globalError &&
{globalError}
}
+
+
+
+
+ router.replace(`/login?next=${encodeURIComponent(nextUrl)}`)} size="small" variant="default">이전
+ 회원가입
+
+
+ router.replace(`/login?next=${encodeURIComponent(nextUrl)}`)} size="small" variant="default" fullWidth>이전
+ 회원가입
+
+
+
+
+ ) : null}
+
+ >
+ )
+}
diff --git a/src/app/sitemap.js b/src/app/sitemap.ts
similarity index 91%
rename from src/app/sitemap.js
rename to src/app/sitemap.ts
index 073582e..2c5bdb1 100644
--- a/src/app/sitemap.js
+++ b/src/app/sitemap.ts
@@ -12,7 +12,7 @@ export default function sitemap() {
priority: 1,
},
{
- url: `${baseUrl}/recruit`,
+ url: `${baseUrl}/recruit/member`,
lastModified: currentDate,
changeFrequency: 'weekly',
priority: 0.8,
diff --git a/src/app/test/login_test/page.jsx b/src/app/test/login_test/page.jsx
deleted file mode 100644
index 7c4ae59..0000000
--- a/src/app/test/login_test/page.jsx
+++ /dev/null
@@ -1,87 +0,0 @@
-'use client';
-
-import { useMemo, useState } from 'react';
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-import { useAuth } from '@/hooks/useAuth';
-import { GoogleLogin } from '@/services/auth/signin/google/GoogleLogin';
-
-const NEXT_PATH = '/test/login_test';
-
-export default function LoginTestPage() {
- const { apiClient } = useAuthenticatedApi();
- const { accessToken } = useAuth();
- const { handleGoogleLogin } = GoogleLogin();
-
- const isLoggedIn = Boolean(accessToken);
- const maskedToken = useMemo(() => {
- if (!accessToken) return '-';
- const head = String(accessToken).slice(0, 8);
- const tail = String(accessToken).slice(-6);
- return `${head}...${tail}`;
- }, [accessToken]);
-
- const [loading, setLoading] = useState(false);
- const [httpStatus, setHttpStatus] = useState(null);
- const [payload, setPayload] = useState(null);
- const [errorText, setErrorText] = useState('');
-
- const handleCallTestApi = async () => {
- setLoading(true);
- setErrorText('');
- setPayload(null);
- setHttpStatus(null);
- try {
- const res = await apiClient.get('/test/login_test');
- setHttpStatus(res.status);
- setPayload(res.data);
- } catch (err) {
- const status = err?.response?.status ?? 'NETWORK_ERROR';
- setHttpStatus(status);
- if (err?.response?.data) setPayload(err.response.data);
- setErrorText(String(err?.message || 'request failed'));
- } finally {
- setLoading(false);
- }
- };
-
- return (
-
-
Google Login Test
-
-
- Current login state
-
- {isLoggedIn ? 'Logged in' : 'Not logged in'}
-
- Access token: {maskedToken}
-
-
-
- handleGoogleLogin({ next: NEXT_PATH })}
- className='rounded px-4 py-2 bg-emerald-600 hover:bg-emerald-500'
- >
- Start Google Login
-
-
- {loading ? 'Requesting...' : 'Call Test API (/api/v1/test/login_test)'}
-
-
-
-
- Response
- HTTP Status: {httpStatus ?? '-'}
- {errorText && (
- Error: {errorText}
- )}
-
-{payload ? JSON.stringify(payload, null, 2) : '-'}
-
-
-
- );
-}
diff --git a/src/app/test/page.jsx b/src/app/test/page.jsx
deleted file mode 100644
index bbecc1b..0000000
--- a/src/app/test/page.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-'use client'
-
-import { useState, useMemo } from 'react';
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
-import { useAuth } from '@/hooks/useAuth';
-
-export default function TestAuthPage() {
- const { apiClient } = useAuthenticatedApi();
- const { accessToken } = useAuth();
-
- const isLoggedIn = Boolean(accessToken);
- const maskedToken = useMemo(() => {
- if (!accessToken) return '-';
- const head = String(accessToken).slice(0, 8);
- const tail = String(accessToken).slice(-6);
- return `${head}...${tail}`;
- }, [accessToken]);
-
- const [loading, setLoading] = useState(false);
- const [httpStatus, setHttpStatus] = useState(null);
- const [payload, setPayload] = useState(null);
- const [errorText, setErrorText] = useState('');
-
- const handleCallAuthApi = async () => {
- setLoading(true);
- setErrorText('');
- setPayload(null);
- setHttpStatus(null);
- try {
- const res = await apiClient.get('/recruit/members', {
- params: { page: 0, size: 20, sort: 'createdAt', dir: 'DESC' },
- });
- setHttpStatus(res.status);
- setPayload(res.data);
- } catch (err) {
- const status = err?.response?.status ?? 'NETWORK_ERROR';
- setHttpStatus(status);
- if (err?.response?.data) setPayload(err.response.data);
- setErrorText(String(err?.message || 'request failed'));
- } finally {
- setLoading(false);
- }
- };
-
- return (
-
-
로그인 테스트
-
cicd 배포 테스트 입니다.
-
-
- 현재 로그인 상태
-
- {isLoggedIn ? '로그인됨' : '미로그인'}
-
- 액세스 토큰: {maskedToken}
-
-
-
-
- {loading ? '요청 중...' : '인증 API 호출 (/api/v1/recruit/members)'}
-
-
-
-
- 응답
- HTTP Status: {httpStatus ?? '-'}
- {errorText && (
- 에러: {errorText}
- )}
-
-{JSON.stringify(payload, null, 2) || '-'}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/unauthorized.js b/src/app/unauthorized.js
deleted file mode 100644
index 22380b3..0000000
--- a/src/app/unauthorized.js
+++ /dev/null
@@ -1,78 +0,0 @@
-// this is experimental page for nextjs
-'use client';
-
-import { useRouter } from "next/navigation";
-import { Button } from "@nextui-org/react";
-import Image from 'next/image';
-
-// resource
-import gdgocIcon from "@public/icons/logo.png";
-
-export default function Unauthorized() {
- const router = useRouter();
-
- const handleClick = () => {
- router.push("/auth/signin");
- };
-
- return (
-
- {/* 아이콘 배경 */}
-
-
- {/* 401 에러 박스 */}
-
-
- {/* 401 아이콘 */}
-
-
- {/* 401 내용 */}
-
- 401 Unauthorized
-
-
-
-
-
로그인이 필요한 페이지입니다.
-
로그인 후 다시 시도해주세요.
-
-
- {/* 버튼 */}
-
- 로그인하기
-
-
- {/* 작은 로고 */}
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/app/unauthorized.tsx b/src/app/unauthorized.tsx
new file mode 100644
index 0000000..5d2d638
--- /dev/null
+++ b/src/app/unauthorized.tsx
@@ -0,0 +1,111 @@
+// this is experimental page for nextjs
+'use client'
+
+import { useMemo } from 'react'
+import { useRouter, useSearchParams } from 'next/navigation'
+import { Button } from '@nextui-org/react'
+import Image from 'next/image' // resource
+import gdgocIcon from '@public/icons/logo.png'
+
+export default function Unauthorized() {
+ const router = useRouter()
+ const searchParams = useSearchParams()
+
+ const nextTarget = useMemo(() => {
+ const raw = searchParams?.get('next')
+ if (raw && raw.startsWith('/')) {
+ return raw
+ }
+ return '/'
+ }, [searchParams])
+
+ const handleClick = () => {
+ if (nextTarget) {
+ router.push(`/login?next=${encodeURIComponent(nextTarget)}`)
+ } else {
+ router.push('/login')
+ }
+ }
+
+ return (
+
+ {/* 아이콘 배경 */}
+
+
+ {/* 401 에러 박스 */}
+
+
+ {/* 401 아이콘 */}
+
+
+ {/* 401 내용 */}
+
+ 401 Unauthorized
+
+
+
+
+
로그인이 필요한 페이지입니다.
+
로그인 후 다시 시도해주세요.
+
+
+ {/* 버튼 */}
+
+ 로그인하기
+
+
+ {/* 작은 로고 */}
+
+
+
+
+
+
+ )
+}
diff --git a/src/components/admin/AdminDashboard.jsx b/src/components/admin/AdminDashboard.tsx
similarity index 100%
rename from src/components/admin/AdminDashboard.jsx
rename to src/components/admin/AdminDashboard.tsx
diff --git a/src/components/admin/AdminTableBottomContent.jsx b/src/components/admin/AdminTableBottomContent.tsx
similarity index 100%
rename from src/components/admin/AdminTableBottomContent.jsx
rename to src/components/admin/AdminTableBottomContent.tsx
diff --git a/src/components/admin/AdminTableCell.jsx b/src/components/admin/AdminTableCell.tsx
similarity index 100%
rename from src/components/admin/AdminTableCell.jsx
rename to src/components/admin/AdminTableCell.tsx
diff --git a/src/components/admin/AdminTableTopContent.jsx b/src/components/admin/AdminTableTopContent.jsx
deleted file mode 100644
index d305316..0000000
--- a/src/components/admin/AdminTableTopContent.jsx
+++ /dev/null
@@ -1,53 +0,0 @@
-"use client";
-
-import React from 'react';
-import { Input } from '@nextui-org/react';
-import { IoSearch } from 'react-icons/io5';
-
-export default function AdminTableTopContent({ searchValue, setSearchValue, onSearch }) {
- return (
-
- }
- value={searchValue}
- onChange={(e) => setSearchValue(e.target.value)}
- onKeyDown={(e) => {
- if (e.key === 'Enter' && !e.nativeEvent.isComposing) {
- e.preventDefault();
- onSearch();
- }
- }}
- onClear={() => setSearchValue('')}
- />
- );
-}
-
-
diff --git a/src/components/admin/AdminTableTopContent.tsx b/src/components/admin/AdminTableTopContent.tsx
new file mode 100644
index 0000000..0b6391b
--- /dev/null
+++ b/src/components/admin/AdminTableTopContent.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import React from 'react';
+import { GdgInput } from '@/components/ui/input/GdgInput';
+import { IoSearch } from 'react-icons/io5';
+
+export default function AdminTableTopContent({ searchValue, setSearchValue, onSearch }) {
+ return (
+
+ }
+ value={searchValue}
+ onChange={(e) => setSearchValue(e.target.value)}
+ onKeyDown={(e) => {
+ if (e.key === 'Enter' && !e.nativeEvent.isComposing) {
+ e.preventDefault();
+ onSearch();
+ }
+ }}
+ onClear={() => setSearchValue('')}
+ />
+ );
+}
+
+
diff --git a/src/components/admin/UserDetailsModal.jsx b/src/components/admin/UserDetailsModal.tsx
similarity index 100%
rename from src/components/admin/UserDetailsModal.jsx
rename to src/components/admin/UserDetailsModal.tsx
diff --git a/src/components/auth/ApiCodeGuard.jsx b/src/components/auth/ApiCodeGuard.tsx
similarity index 81%
rename from src/components/auth/ApiCodeGuard.jsx
rename to src/components/auth/ApiCodeGuard.tsx
index 65a59f0..2c2dbba 100644
--- a/src/components/auth/ApiCodeGuard.jsx
+++ b/src/components/auth/ApiCodeGuard.tsx
@@ -3,12 +3,13 @@
import {useEffect, useMemo, useRef, useState} from 'react';
import {usePathname, useRouter, useSearchParams} from 'next/navigation';
import {useAuthenticatedApi} from '@/hooks/useAuthenticatedApi';
+import {AuthorizedFetchProvider} from '@/context/AuthorizedFetchProvider';
import Loader from '@/components/ui/common/Loader';
/**
* ApiCodeGuard
* - /auth/{role}?next=<...>&team=<...>(옵션) 호출해 200(또는 body.code=200)이면 통과
- * - 아니면 로그인(/auth/signin?next=...)으로 보냄
+ * - 아니면 로그인(/login?next=...)으로 보냄
*
* props:
* - requiredRole: 'GUEST'|'MEMBER'|'CORE'|'LEAD'|'ORGANIZER'|'ADMIN'
@@ -20,7 +21,7 @@ export default function ApiCodeGuard({requiredRole, requiredTeam = '', nextOverr
const router = useRouter();
const pathname = usePathname();
const searchParams = useSearchParams();
- const {apiClient} = useAuthenticatedApi();
+ const {apiClient, authorizedFetch} = useAuthenticatedApi();
const [checking, setChecking] = useState(true);
const [allowed, setAllowed] = useState(false);
@@ -37,7 +38,7 @@ export default function ApiCodeGuard({requiredRole, requiredTeam = '', nextOverr
useEffect(() => {
if (!requiredRole) {
// 역할이 없으면 바로 차단
- router.replace(`/auth/signin?next=${nextUrl}`);
+ router.replace(`/login?next=${nextUrl}`);
return;
}
@@ -46,7 +47,7 @@ export default function ApiCodeGuard({requiredRole, requiredTeam = '', nextOverr
const verify = async () => {
try {
// ✅ 권한 체크: /auth/{role}?next=... (&team=... 은 전달된 경우에만)
- const params = {next: decodeURIComponent(nextUrl)};
+ const params: {next: string; team?: string} = {next: decodeURIComponent(nextUrl)};
if (requiredTeam) params.team = requiredTeam;
const res = await apiClient.get(`/auth/${requiredRole}`, {params});
@@ -59,11 +60,11 @@ export default function ApiCodeGuard({requiredRole, requiredTeam = '', nextOverr
if (okHttp && okBody) {
setAllowed(true);
} else {
- router.replace(`/auth/signin?next=${nextUrl}`);
+ router.replace(`/login?next=${nextUrl}`);
}
} catch {
if (!cancelledRef.current) {
- router.replace(`/auth/signin?next=${nextUrl}`);
+ router.replace(`/login?next=${nextUrl}`);
}
} finally {
if (!cancelledRef.current) setChecking(false);
@@ -79,5 +80,5 @@ export default function ApiCodeGuard({requiredRole, requiredTeam = '', nextOverr
if (checking) return ;
if (!allowed) return null;
- return <>{children}>;
-}
\ No newline at end of file
+ return {children} ;
+}
diff --git a/src/components/auth/signup/AdditionalInfoForm.jsx b/src/components/auth/signup/AdditionalInfoForm.jsx
deleted file mode 100644
index 7f5bd2e..0000000
--- a/src/components/auth/signup/AdditionalInfoForm.jsx
+++ /dev/null
@@ -1,125 +0,0 @@
-"use client"
-
-import { Autocomplete, AutocompleteItem, AutocompleteSection, Input } from "@nextui-org/react";
-
-import { majorOptions } from "@/constant/majorOptions";
-
-export default function AdditionalInfoForm({
- major, setMajor,
- studentId, setStudentId,
- phoneNumber, setPhoneNumber
-}) {
- return (
-
-
-
- {majorOptions.map((collegeGroup) => (
-
- {collegeGroup.items.map((majorItem) => (
-
- {majorItem.value}
-
- ))}
-
- ))}
-
-
-
-
-
-
-
-
- {
- const value = e.target.value.replace(/[^0-9]/g, '');
- if (value.length <= 11) {
- let formattedNumber = value;
- if (value.length > 3) {
- formattedNumber = value.slice(0,3) + '-' + value.slice(3);
- }
- if (value.length > 7) {
- formattedNumber = formattedNumber.slice(0,8) + '-' + formattedNumber.slice(8);
- }
- setPhoneNumber(formattedNumber);
- }
- }}
- classNames={{
- label: "!pb-[10px] !text-white",
- inputWrapper: `h-[48px] rounded-full border-2 border-white/50 caret-white bg-transparent !transition !duration-300 !ease-in-out
- group-data-[focus=true]:border-2 group-data-[focus=true]:border-white group-data-[focus=true]:bg-transparent
- group-data-[hover=true]:border-2 group-data-[hover=true]:border-white group-data-[hover=true]:bg-transparent`,
- mainWrapper: "relative",
- input: "!text-white",
- }}
- />
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/auth/signup/AdditionalInfoForm.tsx b/src/components/auth/signup/AdditionalInfoForm.tsx
new file mode 100644
index 0000000..27f3200
--- /dev/null
+++ b/src/components/auth/signup/AdditionalInfoForm.tsx
@@ -0,0 +1,58 @@
+'use client'
+
+import { GdgInput } from '@/components/ui/input/GdgInput'
+import { MajorAutocomplete } from '@/components/ui/input/MajorAutocomplete'
+
+export default function AdditionalInfoForm({
+ major,
+ setMajor,
+ studentId,
+ setStudentId,
+ phoneNumber,
+ setPhoneNumber
+}) {
+ return (
+
+
+
+
+
+
+
+
+ {
+ const value = e.target.value.replace(/[^0-9]/g, '')
+ if (value.length <= 11) {
+ let formattedNumber = value
+ if (value.length > 3) {
+ formattedNumber = value.slice(0, 3) + '-' + value.slice(3)
+ }
+ if (value.length > 7) {
+ formattedNumber = formattedNumber.slice(0, 8) + '-' + formattedNumber.slice(8)
+ }
+ setPhoneNumber(formattedNumber)
+ }
+ }}
+ />
+
+
+ )
+}
diff --git a/src/components/auth/signup/ProfileInfoForm.jsx b/src/components/auth/signup/ProfileInfoForm.jsx
deleted file mode 100644
index d81616b..0000000
--- a/src/components/auth/signup/ProfileInfoForm.jsx
+++ /dev/null
@@ -1,123 +0,0 @@
-"use client"
-
-import { Input } from "@nextui-org/react";
-
-export default function ProfileInfoForm({
- name, setName,
- email, setEmail,
- password, setPassword,
- confirmPassword, setConfirmPassword,
- errors, isEmailValid,
- isNameDisabled, isEmailDisabled, setIsEmailValid
-}) {
-
- const emailChange = (e) => {
- setEmail(e.target.value);
- setIsEmailValid(false);
- }
-
- return (
-
-
-
-
-
-
-
-
- 이미 가입된 이메일입니다!
-
-
-
0}
- errorMessage={errors.map((error, index) => (
-
{error}
- ))}
- classNames={{
- label: "!pb-[10px] !text-white",
- inputWrapper: `h-[48px] rounded-full border-2 border-white/50 caret-white bg-transparent !transition !duration-300 !ease-in-out
- group-data-[focus=true]:border-2 group-data-[focus=true]:border-white group-data-[focus=true]:bg-transparent
- group-data-[hover=true]:border-2 group-data-[hover=true]:border-white group-data-[hover=true]:bg-transparent
- group-data-[invalid=true]:!bg-transparent group-data-[invalid=true]:!border-[#EA4336]`,
- mainWrapper: "relative",
- helperWrapper: "absolute -bottom-12 left-0",
- input: "!text-white",
- errorMessage: "text-[#EA4336]",
- }}
- />
-
-
0}
- errorMessage={
- password !== confirmPassword && confirmPassword.length > 0 ?
-
비밀번호가 일치하지 않습니다.
: ""
- }
- onValueChange={setConfirmPassword}
- classNames={{
- label: "!pb-[10px] !text-white",
- inputWrapper: `h-[48px] rounded-full border-2 border-white/50 caret-white bg-transparent !transition !duration-300 !ease-in-out
- group-data-[focus=true]:border-2 group-data-[focus=true]:border-white group-data-[focus=true]:bg-transparent
- group-data-[hover=true]:border-2 group-data-[hover=true]:border-white group-data-[hover=true]:bg-transparent
- group-data-[invalid=true]:!bg-transparent group-data-[invalid=true]:!border-[#EA4336]`,
- mainWrapper: "relative",
- helperWrapper: "absolute -bottom-6 left-0",
- input: "!text-white",
- errorMessage: "text-[#EA4336]",
- }}
- />
-
- );
-}
\ No newline at end of file
diff --git a/src/components/auth/signup/ProfileInfoForm.tsx b/src/components/auth/signup/ProfileInfoForm.tsx
new file mode 100644
index 0000000..eb109ca
--- /dev/null
+++ b/src/components/auth/signup/ProfileInfoForm.tsx
@@ -0,0 +1,78 @@
+"use client"
+
+import { GdgInput } from "@/components/ui/input/GdgInput";
+
+export default function ProfileInfoForm({
+ name, setName,
+ email, setEmail,
+ password, setPassword,
+ confirmPassword, setConfirmPassword,
+ errors, isEmailValid,
+ isNameDisabled, isEmailDisabled, setIsEmailValid
+}) {
+
+ const emailChange = (e) => {
+ setEmail(e.target.value);
+ setIsEmailValid(false);
+ }
+
+ return (
+
+
+
+
+
+
+
+
+ 이미 가입된 이메일입니다!
+
+
+
0}
+ errorMessage={errors.join(' ')}
+ />
+
+ 0}
+ errorMessage={
+ password !== confirmPassword && confirmPassword.length > 0
+ ? '비밀번호가 일치하지 않습니다.'
+ : ''
+ }
+ onValueChange={setConfirmPassword}
+ />
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/event/homecoming/GuestbookWordCloud.jsx b/src/components/event/homecoming/GuestbookWordCloud.jsx
deleted file mode 100644
index cdaeec7..0000000
--- a/src/components/event/homecoming/GuestbookWordCloud.jsx
+++ /dev/null
@@ -1,65 +0,0 @@
-'use client';
-
-import { useMemo } from "react";
-
-const mapToRange = (value, min, max) => min + value * (max - min);
-
-const defaultColors = {
- highlight: "text-cblue",
- muted: "text-slate-600",
- shadow: "drop-shadow-[0_1.5px_10px_rgba(15,23,42,0.12)]",
-};
-
-export default function GuestbookWordCloud({ entries, isLoading, recentCount = 5, className = "", style = {}, colorScheme = defaultColors }) {
- const words = useMemo(() => {
- if (!entries?.length) {
- return [];
- }
-
- return entries.map((entry, idx) => {
- const key = entry.id ?? `${entry.wristbandSerial ?? "unknown"}-${idx}`;
- const goldenRatio = 0.61803398875;
- const base = Math.random() + idx * goldenRatio;
- const top = mapToRange(base % 1, 12, 88);
- const left = mapToRange((base * goldenRatio) % 1, 10, 90);
- const fontSize = mapToRange(Math.random(), 1.2, 3.2);
- const rotate = mapToRange(Math.random(), -10, 10);
- const opacity = mapToRange(Math.random(), 0.45, 0.95);
-
- return {
- key,
- label: entry.name,
- isRecent: idx >= Math.max(entries.length - recentCount, 0),
- style: {
- top: `${top}%`,
- left: `${left}%`,
- fontSize: `${fontSize}rem`,
- opacity,
- transform: `translate(-50%, -50%) rotate(${rotate}deg)`,
- },
- };
- });
- }, [entries, recentCount]);
-
- return (
-
- {words.length ? (
- words.map((word) => (
-
- {word.label}
-
- ))
- ) : (
- !isLoading && (
-
- 아직 등록된 입장 정보가 없습니다.
-
- )
- )}
-
- );
-}
diff --git a/src/components/event/homecoming/GuestbookWordCloud.tsx b/src/components/event/homecoming/GuestbookWordCloud.tsx
new file mode 100644
index 0000000..9275f0e
--- /dev/null
+++ b/src/components/event/homecoming/GuestbookWordCloud.tsx
@@ -0,0 +1,70 @@
+'use client'
+
+import { useMemo } from 'react'
+
+const mapToRange = (value, min, max) => min + value * (max - min)
+
+const defaultColors = {
+ highlight: 'text-blue',
+ muted: 'text-slate-600',
+ shadow: 'drop-shadow-[0_1.5px_10px_rgba(15,23,42,0.12)]'
+}
+
+export default function GuestbookWordCloud({
+ entries,
+ isLoading,
+ recentCount = 5,
+ className = '',
+ style = {},
+ colorScheme = defaultColors
+}) {
+ const words = useMemo(() => {
+ if (!entries?.length) {
+ return []
+ }
+
+ return entries.map((entry, idx) => {
+ const key = entry.id ?? `${entry.wristbandSerial ?? 'unknown'}-${idx}`
+ const goldenRatio = 0.61803398875
+ const base = Math.random() + idx * goldenRatio
+ const top = mapToRange(base % 1, 12, 88)
+ const left = mapToRange((base * goldenRatio) % 1, 10, 90)
+ const fontSize = mapToRange(Math.random(), 1.2, 3.2)
+ const rotate = mapToRange(Math.random(), -10, 10)
+ const opacity = mapToRange(Math.random(), 0.45, 0.95)
+
+ return {
+ key,
+ label: entry.name,
+ isRecent: idx >= Math.max(entries.length - recentCount, 0),
+ style: {
+ top: `${top}%`,
+ left: `${left}%`,
+ fontSize: `${fontSize}rem`,
+ opacity,
+ transform: `translate(-50%, -50%) rotate(${rotate}deg)`
+ }
+ }
+ })
+ }, [entries, recentCount])
+
+ return (
+
+ {words.length
+ ? words.map((word) => (
+
+ {word.label}
+
+ ))
+ : !isLoading && (
+
+ 아직 등록된 입장 정보가 없습니다.
+
+ )}
+
+ )
+}
diff --git a/src/components/landing/AboutSection.jsx b/src/components/landing/AboutSection.jsx
deleted file mode 100644
index 2fe97df..0000000
--- a/src/components/landing/AboutSection.jsx
+++ /dev/null
@@ -1,113 +0,0 @@
-import React, { useEffect } from 'react';
-import gsap from 'gsap';
-import ScrollTrigger from 'gsap/ScrollTrigger';
-
-gsap.registerPlugin(ScrollTrigger);
-
-export default function AboutSection() {
- useEffect(() => {
- const ctx = gsap.context(() => {
- // 섹션2 스크롤 애니메이션 타임라인 설정
- let tl = gsap.timeline({
- scrollTrigger: {
- trigger: '#section2', // 트리거 요소
- start: 'top top', // 시작 지점
- end: '+=200%', // 종료 지점 (200% 스크롤)
- scrub: true, // 스크롤에 따른 부드러운 애니메이션
- pin: true, // 섹션 고정
- },
- });
-
- // GDGOC 네온 텍스트 깜빡임 효과
- const neonLetters = document.querySelectorAll('#section2 .neon1');
-
- // 첫 번째 깜빡임 효과
- tl.to(
- neonLetters,
- {
- textShadow: '0 0 10px currentColor, 0 0 20px currentColor, 0 0 30px currentColor',
- duration: 0.1,
- yoyo: true,
- repeat: 3,
- ease: 'steps(2)',
- stagger: {
- each: 0.05,
- from: 'random',
- },
- },
- 0
- );
-
- // 두 번째 깜빡임 효과
- tl.to(
- neonLetters,
- {
- textShadow: '0 0 10px currentColor, 0 0 20px currentColor, 0 0 30px currentColor',
- duration: 0.15,
- yoyo: true,
- repeat: 2,
- ease: 'steps(1)',
- stagger: {
- each: 0.03,
- from: 'random',
- },
- },
- 0.5
- );
-
- // 텍스트 페이드인 애니메이션
- tl.fromTo('#section2-text1', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 0.2) // 첫 번째 텍스트
- .fromTo('#section2-text2', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 0.7) // 두 번째 텍스트
- .fromTo('#section2-text3', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 1.2) // 세 번째 텍스트
- .fromTo('#section2-text4', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 1.7); // 네 번째 텍스트
-
- // 컴포넌트 언마운트 시 정리
- return () => {
- ScrollTrigger.getAll().forEach((trigger) => trigger.kill()); // 모든 ScrollTrigger 제거
- };
- });
-
- return () => {
- ctx.revert();
- ScrollTrigger.getAll().forEach(st => st.kill());
- gsap.killTweensOf("*"); // 모든 애니메이션 정리
- };
- }, []);
-
- return (
-
-
-
-
- G
- D
- G
- o
- C
- INHA
-
- , 어떤 곳인가요?
-
-
-
- 개발 에 관심 있는 사람들이 모여{' '}
-
- 네트워킹 하고,
-
-
- 다양한 프로젝트에 참여하며
- 함께 성장 하는 공간입니다.
-
-
- 비개발자부터 숙련된 개발자까지
- 누구나 함께 할 수 있습니다.
-
-
- GDGoC라는 글로벌 IT 무대에서
- 끊임없이 기회를 찾고 성장 해보세요!
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/landing/AboutSection.tsx b/src/components/landing/AboutSection.tsx
new file mode 100644
index 0000000..34bbb13
--- /dev/null
+++ b/src/components/landing/AboutSection.tsx
@@ -0,0 +1,113 @@
+import React, { useEffect } from 'react'
+import gsap from 'gsap'
+import ScrollTrigger from 'gsap/ScrollTrigger'
+
+gsap.registerPlugin(ScrollTrigger)
+
+export default function AboutSection() {
+ useEffect(() => {
+ const ctx = gsap.context(() => {
+ // 섹션2 스크롤 애니메이션 타임라인 설정
+ let tl = gsap.timeline({
+ scrollTrigger: {
+ trigger: '#section2', // 트리거 요소
+ start: 'top top', // 시작 지점
+ end: '+=200%', // 종료 지점 (200% 스크롤)
+ scrub: true, // 스크롤에 따른 부드러운 애니메이션
+ pin: true // 섹션 고정
+ }
+ })
+
+ // GDGOC 네온 텍스트 깜빡임 효과
+ const neonLetters = document.querySelectorAll('#section2 .neon1')
+
+ // 첫 번째 깜빡임 효과
+ tl.to(
+ neonLetters,
+ {
+ textShadow: '0 0 10px currentColor, 0 0 20px currentColor, 0 0 30px currentColor',
+ duration: 0.1,
+ yoyo: true,
+ repeat: 3,
+ ease: 'steps(2)',
+ stagger: {
+ each: 0.05,
+ from: 'random'
+ }
+ },
+ 0
+ )
+
+ // 두 번째 깜빡임 효과
+ tl.to(
+ neonLetters,
+ {
+ textShadow: '0 0 10px currentColor, 0 0 20px currentColor, 0 0 30px currentColor',
+ duration: 0.15,
+ yoyo: true,
+ repeat: 2,
+ ease: 'steps(1)',
+ stagger: {
+ each: 0.03,
+ from: 'random'
+ }
+ },
+ 0.5
+ )
+
+ // 텍스트 페이드인 애니메이션
+ tl.fromTo('#section2-text1', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 0.2) // 첫 번째 텍스트
+ .fromTo('#section2-text2', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 0.7) // 두 번째 텍스트
+ .fromTo('#section2-text3', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 1.2) // 세 번째 텍스트
+ .fromTo('#section2-text4', { opacity: 0, y: 20 }, { opacity: 1, y: 0 }, 1.7) // 네 번째 텍스트
+
+ // 컴포넌트 언마운트 시 정리
+ return () => {
+ ScrollTrigger.getAll().forEach((trigger) => trigger.kill()) // 모든 ScrollTrigger 제거
+ }
+ })
+
+ return () => {
+ ctx.revert()
+ ScrollTrigger.getAll().forEach((st) => st.kill())
+ gsap.killTweensOf('*') // 모든 애니메이션 정리
+ }
+ }, [])
+
+ return (
+
+
+
+
+ G
+ D
+ G
+ o
+ C
+ INHA
+
+ , 어떤 곳인가요?
+
+
+
+ 개발 에 관심 있는 사람들이 모여{' '}
+
+ 네트워킹 하고,
+
+
+ 다양한 프로젝트에 참여하며
+ 함께 성장 하는 공간입니다.
+
+
+ 비개발자부터 숙련된 개발자까지
+ 누구나 함께 할 수 있습니다.
+
+
+ GDGoC라는 글로벌 IT 무대에서
+ 끊임없이 기회를 찾고 성장 해보세요!
+
+
+
+
+ )
+}
diff --git a/src/components/landing/ActivitiesSection.jsx b/src/components/landing/ActivitiesSection.jsx
deleted file mode 100644
index eb0684d..0000000
--- a/src/components/landing/ActivitiesSection.jsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import React, { useState, useEffect } from 'react';
-import Image from 'next/image';
-import gsap from 'gsap';
-
-import seminar from '@public/images/activity/seminar.jpg';
-import snack from '@public/images/activity/snack.jpg';
-import party from '@public/images/activity/party.jpg';
-import conf from '@public/images/activity/conf.jpg';
-import googleconf from '@public/images/activity/googleconf.jpg';
-import christmas from '@public/images/activity/christmas.jpg';
-
-function ActivitiesSection() {
-
- const [hoveredCard, setHoveredCard] = useState(1);
-
- const handleMouseEnter = (cardIndex) => {
- setHoveredCard(cardIndex);
- };
-
- const handleMouseLeave = () => {
- setHoveredCard(null);
- };
-
- const cards = [
- {title: '연사자 초청', description: '실전활용도 높은 온/오프 강연', image: seminar},
- {title: '간식드리미', description: '정성 가득 시험기간 간식드리미', image: snack},
- {title: '네트워킹 행사', description: 'MT/한강/잔망 등 다같이 외부 나들이', image: party},
- {title: '내부 행사', description: '자체•연합 해커톤 주최 및 참여', image: conf},
- {title: '외부행사', description: 'Google 등 IT기업의 네트워킹/레퍼런스 참여', image: googleconf},
- {title: '크리스마스 파티', description: '연말 파티와 함께하는 네트워킹', image: christmas},
- ]
-
-
- useEffect(() => {
- gsap.fromTo(
- '#cards',
- {
- y: 90,
- opacity: 0,
- },
- {
- y: 0,
- opacity: 1,
- duration: 1,
- delay: 0.5,
- ease: 'power2.out',
- scrollTrigger: {
- trigger: '#section4',
- start: 'top center',
- toggleActions: 'play none none reverse',
- },
- }
- );
- }, []);
-
-
- return (
-
-
-
❓
-
합류하면 어떤 활동들을 할 수 있나요?
-
-
-
- {cards.map((card, index) => (
-
handleMouseEnter(index)}
- onMouseLeave={handleMouseLeave}
- >
-
-
-
-
-
{card.title}
-
{card.description}
-
-
-
-
- ))}
-
-
-
- );
-}
-
-export default ActivitiesSection;
\ No newline at end of file
diff --git a/src/components/landing/ActivitiesSection.tsx b/src/components/landing/ActivitiesSection.tsx
new file mode 100644
index 0000000..af0d9ce
--- /dev/null
+++ b/src/components/landing/ActivitiesSection.tsx
@@ -0,0 +1,99 @@
+import React, { useState, useEffect } from 'react'
+import Image from 'next/image'
+import gsap from 'gsap'
+
+import seminar from '@public/images/activity/seminar.jpg'
+import snack from '@public/images/activity/snack.jpg'
+import party from '@public/images/activity/party.jpg'
+import conf from '@public/images/activity/conf.jpg'
+import googleconf from '@public/images/activity/googleconf.jpg'
+import christmas from '@public/images/activity/christmas.jpg'
+
+function ActivitiesSection() {
+ const [hoveredCard, setHoveredCard] = useState(1)
+
+ const handleMouseEnter = (cardIndex) => {
+ setHoveredCard(cardIndex)
+ }
+
+ const handleMouseLeave = () => {
+ setHoveredCard(null)
+ }
+
+ const cards = [
+ { title: '연사자 초청', description: '실전활용도 높은 온/오프 강연', image: seminar },
+ { title: '간식드리미', description: '정성 가득 시험기간 간식드리미', image: snack },
+ { title: '네트워킹 행사', description: 'MT/한강/잔막 등 다같이 외부 나들이', image: party },
+ { title: '내부 행사', description: '자체•연합 해커톤 주최 및 참여', image: conf },
+ {
+ title: '외부 행사',
+ description: 'Google 등 IT기업의 네트워킹/레퍼런스 참여',
+ image: googleconf
+ },
+ { title: '크리스마스 파티', description: '연말 파티와 함께하는 네트워킹', image: christmas }
+ ]
+
+ useEffect(() => {
+ gsap.fromTo(
+ '#cards',
+ {
+ y: 90,
+ opacity: 0
+ },
+ {
+ y: 0,
+ opacity: 1,
+ duration: 1,
+ delay: 0.5,
+ ease: 'power2.out',
+ scrollTrigger: {
+ trigger: '#section4',
+ start: 'top center',
+ toggleActions: 'play none none reverse'
+ }
+ }
+ )
+ }, [])
+
+ return (
+
+
+
❓
+
합류하면 어떤 활동들을 할 수 있나요?
+
+
+
+ {cards.map((card, index) => (
+
handleMouseEnter(index)}
+ onMouseLeave={handleMouseLeave}
+ >
+
+
+
+
+
{card.title}
+
{card.description}
+
+
+
+
+ ))}
+
+
+
+ )
+}
+
+export default ActivitiesSection
diff --git a/src/components/landing/CTASection.jsx b/src/components/landing/CTASection.jsx
deleted file mode 100644
index c760f38..0000000
--- a/src/components/landing/CTASection.jsx
+++ /dev/null
@@ -1,32 +0,0 @@
-import { Button } from '@nextui-org/react';
-
-function CTASection({ router }) {
-
- return (
-
-
-
- G
- D
- G
- o
- C 와 함께
-
-
- 변화하는 나를 만나보세요
-
-
-
router.push('/recruit')}
- radius='full'
- className='w-64 max-w-full h-14 mobile:w-40 mobile:h-12 mobile:text-2xl bg-gradient-to-r from-[#EA4335] to-[#FF6E62] text-white text-3xl relative group'
- >
-
- 지원하기
-
-
-
- );
-}
-
-export default CTASection;
\ No newline at end of file
diff --git a/src/components/landing/CTASection.tsx b/src/components/landing/CTASection.tsx
new file mode 100644
index 0000000..7a937ab
--- /dev/null
+++ b/src/components/landing/CTASection.tsx
@@ -0,0 +1,40 @@
+import { Button } from '@nextui-org/react'
+
+function CTASection({ router }) {
+ return (
+
+
+
+ G
+ D
+ G
+ o
+ C 와 함께
+
+
+ 변화하는 나를 만나보세요
+
+
+
router.push('/recruit/member')}
+ radius="full"
+ className="w-64 max-w-full h-14 mobile:w-40 mobile:h-12 mobile:text-2xl bg-linear-to-r from-red to-yellow text-white text-3xl relative group overflow-hidden"
+ >
+
+ 지원하기
+
+
+ 지원 후 일정이 궁금하시다면?{' '}
+ router.push('/recruit/member/completed')}
+ className="underline underline-offset-4 hover:text-white transition-colors"
+ >
+ 일정 안내 바로가기
+
+
+
+
+ )
+}
+
+export default CTASection
diff --git a/src/components/landing/HeroSection.jsx b/src/components/landing/HeroSection.jsx
deleted file mode 100644
index e7fe18d..0000000
--- a/src/components/landing/HeroSection.jsx
+++ /dev/null
@@ -1,96 +0,0 @@
-import React, { useRef, useEffect } from "react";
-import NextImage from "next/image";
-import { Button } from "@nextui-org/react";
-import gsap from 'gsap';
-
-import Header from "@/components/ui/common/Header";
-
-import bg from "@public/images/bgimg.png";
-
-export default function HeroSection({ router }) {
- const subTitleRef = useRef(null);
- const buttonRef = useRef(null);
-
- const animation = () => {
- TypeHangul.type("#typing-effect", {
- text: "개발자와 비개발자가 같이 성장하는 즐거움 with Google",
- speed: 27,
- intervalType: 20,
- humanize: 0.02,
- });
-
- const typingEffect = document.getElementById("typing-effect");
- typingEffect.addEventListener("th.endType", () => {
- gsap.fromTo(
- subTitleRef.current,
- { y: 50, opacity: 0 },
- { y: 0, opacity: 1, duration: 1, ease: "power1.inOut" }
- );
-
- gsap.fromTo(
- buttonRef.current,
- { y: 50, opacity: 0 },
- { y: 0, opacity: 1, duration: 1, delay: 0.4, ease: "power1.out",
- onComplete: () => {
- gsap.to(buttonRef.current, {
- y: "-=10",
- duration: 1.5,
- repeat: -1,
- yoyo: true,
- ease: "power1.inOut",
- });
- },
- }
- );
- });
- }
-
- useEffect(() => {
- if (typeof window !== "undefined" && window.TypeHangul) {
- animation();
- } else {
- setTimeout(animation, 100);
- }
- }, []);
-
- return (
-
-
-
-
-
-
-
-
- G
- D
- G
- o
- C
-
- INHA
-
-
-
- 2025-2 Recruitment
-
-
-
router.push("/recruit")}
- radius="full"
- ref={buttonRef}
- className="opacity-0 mt-[41px] w-60 h-16 mobile:w-40 mobile:h-14 mobile:text-2xl bg-gradient-to-r from-[#EA4335] to-[#FF6E62] text-white text-3xl relative group"
- >
- 지원하기
-
-
-
- );
-}
diff --git a/src/components/landing/HeroSection.tsx b/src/components/landing/HeroSection.tsx
new file mode 100644
index 0000000..217ebd2
--- /dev/null
+++ b/src/components/landing/HeroSection.tsx
@@ -0,0 +1,129 @@
+import React, { useRef, useEffect } from 'react'
+import NextImage from 'next/image'
+import { Button } from '@nextui-org/react'
+import gsap from 'gsap'
+
+import Header from '@/components/ui/common/Header'
+
+import bg from '@public/images/bgimg.png'
+
+export default function HeroSection({ router }) {
+ const subTitleRef = useRef(null)
+ const buttonRef = useRef(null)
+ const linkRef = useRef(null)
+
+ const animation = (typeHangul: any) => {
+ typeHangul.type('#typing-effect', {
+ text: '개발자와 비개발자가 같이 성장하는 즐거움 with Google',
+ speed: 27,
+ intervalType: 20,
+ humanize: 0.02
+ })
+
+ const typingEffect = document.getElementById('typing-effect')
+ typingEffect.addEventListener('th.endType', () => {
+ gsap.fromTo(
+ subTitleRef.current,
+ { y: 50, opacity: 0 },
+ { y: 0, opacity: 1, duration: 1, ease: 'power1.inOut' }
+ )
+
+ gsap.fromTo(
+ buttonRef.current,
+ { y: 50, opacity: 0 },
+ {
+ y: 0,
+ opacity: 1,
+ duration: 1,
+ delay: 0.4,
+ ease: 'power1.out',
+ onComplete: () => {
+ gsap.to(buttonRef.current, {
+ y: '-=10',
+ duration: 1.5,
+ repeat: -1,
+ yoyo: true,
+ ease: 'power1.inOut'
+ })
+ }
+ }
+ )
+
+ gsap.fromTo(
+ linkRef.current,
+ { y: 20, opacity: 0 },
+ { y: 0, opacity: 1, duration: 0.8, delay: 0.8, ease: 'power1.out' }
+ )
+ })
+ }
+
+ useEffect(() => {
+ let cancelled = false
+
+ const run = async () => {
+ const typeHangulModule = await import('type-hangul/src')
+ if (cancelled) return
+ const typeHangul = typeHangulModule.default
+ if (typeHangul?.type) {
+ animation(typeHangul)
+ }
+ }
+
+ void run()
+
+ return () => {
+ cancelled = true
+ }
+ }, [])
+
+ return (
+
+
+
+
+
+
+
+
+ G
+ D
+ G
+ o
+ C
+
+ INHA
+
+
+
+ 2026-1 Recruitment
+
+
+
router.push('/recruit/member')}
+ radius="full"
+ ref={buttonRef}
+ className="opacity-0 mt-[41px] w-60 h-16 mobile:w-40 mobile:h-14 mobile:text-2xl bg-gradient-to-r from-red to-yellow text-white text-3xl relative group"
+ >
+ 지원하기
+
+
+ 지원 후 일정이 궁금하시다면?{' '}
+ router.push('/recruit/member/completed')}
+ className="underline underline-offset-4 hover:text-white transition-colors"
+ >
+ 일정 안내 바로가기
+
+
+
+
+ )
+}
diff --git a/src/components/landing/PartnersSection.jsx b/src/components/landing/PartnersSection.tsx
similarity index 86%
rename from src/components/landing/PartnersSection.jsx
rename to src/components/landing/PartnersSection.tsx
index d2c9cad..7e3a60d 100644
--- a/src/components/landing/PartnersSection.jsx
+++ b/src/components/landing/PartnersSection.tsx
@@ -47,12 +47,12 @@ function PartnersSection() {
-
- G
- D
- G
- o
- C
+
+ G
+ D
+ G
+ o
+ C
INHA
와 함께하는 단체
@@ -76,4 +76,4 @@ function PartnersSection() {
);
}
-export default PartnersSection;
\ No newline at end of file
+export default PartnersSection;
diff --git a/src/components/landing/StatsSection.jsx b/src/components/landing/StatsSection.tsx
similarity index 79%
rename from src/components/landing/StatsSection.jsx
rename to src/components/landing/StatsSection.tsx
index b6384a9..31dcc4d 100644
--- a/src/components/landing/StatsSection.jsx
+++ b/src/components/landing/StatsSection.tsx
@@ -5,14 +5,14 @@ import gsap from 'gsap';
function StatsSection() {
const stats = [
- { title: '전세계' , number: 1863, unit: '+', color: 'red' },
- { title: '참여국가' , number: 111, unit: '개국', color: 'green' },
+ { title: '전 세계' , number: 1863, unit: '+', color: 'red' },
+ { title: '참여 국가' , number: 111, unit: '개국', color: 'green' },
{ title: '국내' , number: 36, unit: '대학', color: 'yellow' },
{ title: '현재 멤버' , number: 102, unit: '명', color: 'blue' },
];
useEffect(() => {
- const numberElements = gsap.utils.toArray('.number-animation');
+ const numberElements = gsap.utils.toArray('.number-animation');
numberElements.forEach((element, index) => {
gsap.set(element, { textContent: '0' });
@@ -34,7 +34,7 @@ function StatsSection() {
{stats.map((stat, index) => (
-
{stat.title}
+
{stat.title}
{stat.number}
{stat.unit}
@@ -45,4 +45,4 @@ function StatsSection() {
);
}
-export default StatsSection;
\ No newline at end of file
+export default StatsSection;
diff --git a/src/components/landing/StudySection.jsx b/src/components/landing/StudySection.jsx
deleted file mode 100644
index 4e9f314..0000000
--- a/src/components/landing/StudySection.jsx
+++ /dev/null
@@ -1,94 +0,0 @@
-import React, { useEffect } from 'react';
-import Image from 'next/image';
-import gsap from 'gsap';
-
-import { Splide, SplideSlide } from '@splidejs/splide';
-import { AutoScroll } from '@splidejs/splide-extension-auto-scroll';
-import '@splidejs/splide/css';
-
-import study1 from '@public/images/study/study1.jpg';
-import study2 from '@public/images/study/study2.jpg';
-import study3 from '@public/images/study/study3.jpg';
-import study4 from '@public/images/study/study4.jpg';
-import study5 from '@public/images/study/study5.jpg';
-
-function StudySection() {
-
- const study = [
- {title: '백엔드 스터디', image: study1},
- {title: '인공지능 스터디', image: study2},
- {title: '파이썬 기초 스터디', image: study3},
- {title: '파이썬 데이터 분석 스터디', image: study4},
- {title: 'UX/UI 스터디', image: study5},
- ]
-
- useEffect(() => {
- const splide = new Splide('#card-carousel', {
- type: 'loop',
- drag: 'free',
- focus: 'center',
- perPage: 4,
- gap: '2rem',
- breakpoints: {
- 1024: {
- perPage: 3,
- gap: '1.5rem',
- },
- 768: {
- perPage: 2,
- gap: '1rem',
- },
- 480: {
- perPage: 1,
- gap: '0.5rem',
- },
- },
- autoScroll: {
- speed: 1.5,
- },
- arrows: false,
- pagination: false,
- });
-
- splide.mount({ AutoScroll });
-
- return () => {
- splide.destroy();
- };
- }, []);
-
- return (
-
-
이런 공부, 같이 해보실래요?
-
-
-
- {study.map((item, index) => (
-
-
-
-
-
-
- {item.title}
-
-
-
- ))}
-
-
-
-
- );
-}
-
-export default StudySection;
\ No newline at end of file
diff --git a/src/components/main/CardCarousel.jsx b/src/components/main/CardCarousel.jsx
deleted file mode 100644
index 32dc63c..0000000
--- a/src/components/main/CardCarousel.jsx
+++ /dev/null
@@ -1,51 +0,0 @@
-import { useEffect, useRef } from 'react';
-import { Splide } from '@splidejs/splide';
-
-import '@splidejs/splide/css';
-
-export default function CardCarousel({ id, title, children }) {
- const splideRef = useRef(null);
-
- useEffect(() => {
- if (splideRef.current) {
- const cardSplide = new Splide(splideRef.current, {
- type: 'slide',
- perPage: 3,
- gap: '3rem',
- arrows: true,
- pagination: false,
- drag: true,
- snap: true,
- breakpoints: {
- 1024: {
- perPage: 3,
- gap: '1.5rem',
- },
- 768: {
- perPage: 1,
- gap: '1rem',
- }
- },
- });
-
- cardSplide.mount();
-
- return () => {
- cardSplide.destroy();
- };
- }
- }, []);
-
- return (
- <>
-
{title}
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/components/main/EventCard.jsx b/src/components/main/EventCard.jsx
deleted file mode 100644
index 0a8b9a1..0000000
--- a/src/components/main/EventCard.jsx
+++ /dev/null
@@ -1,45 +0,0 @@
-import Image from 'next/image';
-import {Button, Card, CardBody, CardFooter, CardHeader} from "@nextui-org/react";
-
-export default function EventCard({ logo, title, statusLabel, statusColor, eventType, eventTypeColor, description, details, isHidden }) {
- return (
- <>
-
-
-
- {title}
-
-
-
-
- {statusLabel}
-
-
- {eventType}
-
-
-
- {description}
-
-
-
-
목적
-
{details.purpose}
-
-
-
일정
-
{details.schedule}
-
-
-
-
-
- 자세히 보기
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/components/main/FAQ.jsx b/src/components/main/FAQ.jsx
deleted file mode 100644
index a021d1c..0000000
--- a/src/components/main/FAQ.jsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import React from 'react';
-import { Accordion, AccordionItem } from "@nextui-org/react";
-
-export default function FAQ() {
- const faqItems = [
- {
- key: "1",
- color: "#EA4335",
- question: "GDGoC는 어떤 동아리인가요?",
- answer: `2010년대부터 Google은 자사의 개발 언어와 기술들을 좋아하는 개발자, 비개발자들을 연결하여 커뮤니티를 만들고 커뮤니티 구성원들을 기반으로 더 많은 사람들이 연결되어 구글의 기술 생태계에 참여할 수 있길 바라왔습니다. 그렇게 한국을 포함한 전 세계 구글 본사에 구글 개발자 생태계 팀이 창설되었습니다. \n\n이후 Amazon, Microsoft, Apple 등의 빅테크 기업들도 줄지어 커뮤니티 생태계 구축에 힘쓰기 시작했습니다. 선두주자였던 Google 커뮤니티들은 현재까지도 전 세계에서 가장 큰 커뮤니티를 형성하고 활발하게 사람들을 연결하고 있습니다. \n 이후 on Campus 라는 이름으로, 대학교에서의 개발자 커뮤니티도 창설하였고, 이것이 지금의 GDGoC 입니다.`
- },
- {
- key: "2",
- color: "#34A853",
- question: "프로그래밍 기초 지식이 전혀 없는 상태인데 어떤 활동에 참여할 수 있나요?",
- answer: 'GDGoC INHA는 개발자와 비개발자가 함께 할 수 있는 커뮤니티를 목표로 두고 있습니다. 이에, 프로그래밍 기초 지식을 쌓을 수 있는 다양한 스터디 및 세미나에 참석할 수 있고, 초보를 위한 해커톤 또한 매년 개최중입니다. \n\n 이외에도 타 챕터와의 연합 체육대회, 친목 캠프 등 다양한 행사에 참여할 수 있습니다. 홈페이지의 행사 탭을 클릭하시면 보다 더 자세한 내용을 확인할 수 있습니다.'
- },
- {
- key: "3",
- color: "#4285F4",
- question: "정기 활동이 있나요?",
- answer: 'GDGoC INHA는 각 행사를 제외하고도 정기 총회를 진행하고 있습니다. 많은 멤버 수 만큼, 반드시 필참인 정기 활동은 없으며, 매달 마지막주에 정기총회를 열어, 진행상황 보고, 앞으로의 행사들 홍보, 이외 친목 시간등으로 구성된 행사를 진행합니다.'
- },
- {
- key: "4",
- color: "#F9AB00",
- question: "동방은 어디에 있나요?",
- answer: '인하대학교 5호관 동쪽 021호에 있습니다. 누구나 언제나 놀러와서 쉬다가셔도 됩니다!'
- }
- ];
-
- return (
- <>
-
자주 묻는 질문
-
- {faqItems.map((item) => (
- Q}
- title={item.question}
- >
- {item.answer.split('\n\n').map((paragraph, i) => (
-
- {paragraph}
- {i < item.answer.split('\n\n').length - 1 && (
- <>
-
- >
- )}
-
- ))}
-
- ))}
-
- >
- );
-}
\ No newline at end of file
diff --git a/src/components/main/MainCarousel.jsx b/src/components/main/MainCarousel.jsx
deleted file mode 100644
index 52027fa..0000000
--- a/src/components/main/MainCarousel.jsx
+++ /dev/null
@@ -1,67 +0,0 @@
-import { useEffect, useRef } from 'react';
-import Image from 'next/image';
-import Link from 'next/link';
-import { Splide } from '@splidejs/splide';
-import '@splidejs/splide/css';
-
-export default function MainCarousel({ slides }) {
- const splideRef = useRef(null);
-
- useEffect(() => {
- if (splideRef.current) {
- const mainSplide = new Splide(splideRef.current, {
- type: 'loop',
- perPage: 1,
- autoplay: true,
- interval: 5000, // 5초
- pauseOnHover: false,
- arrows: false,
- pagination: true,
- });
-
- mainSplide.mount();
-
- return () => {
- mainSplide.destroy();
- };
- }
- }, []);
-
- return (
-
-
-
- {slides.map((slide, index) => (
-
-
-
-
-
-
-
-
-
-
-
-
-
- {slide.tag1}
-
-
- {slide.tag2}
-
-
-
{slide.title}
-
{slide.description}
-
더 알아보기
-
-
-
-
-
- ))}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/notice/NoticeCard.jsx b/src/components/notice/NoticeCard.jsx
deleted file mode 100644
index 9184e77..0000000
--- a/src/components/notice/NoticeCard.jsx
+++ /dev/null
@@ -1,37 +0,0 @@
-'use client';
-
-import Image from 'next/image';
-
-export default function NoticeCard({ item, onClick }) {
- const statusDot = item.status === 'ongoing' ? 'bg-green-500' : 'bg-red-500';
-
- return (
-
onClick?.(item)}
- className="text-left bg-[#1f1f1f] rounded-2xl overflow-hidden hover:shadow-lg transition-transform hover:-translate-y-0.5 focus:outline-none focus:ring-2 focus:ring-green-500"
- >
-
- {item.image && (
-
- )}
-
-
-
{item.title}
-
{item.summary}
-
-
- {item.tags?.map((t) => (
- #{t}
- ))}
-
-
-
- {item.status === 'ongoing' ? '진행중' : '종료'}
-
-
-
-
- );
-}
-
-
diff --git a/src/components/notice/NoticeModal.jsx b/src/components/notice/NoticeModal.jsx
deleted file mode 100644
index cb8f7a1..0000000
--- a/src/components/notice/NoticeModal.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-'use client';
-
-import Image from 'next/image';
-import ReactMarkdown from 'react-markdown';
-import remarkGfm from 'remark-gfm';
-import remarkBreaks from 'remark-breaks';
-import { Modal, ModalContent, ModalHeader, ModalBody } from '@nextui-org/react';
-
-export default function NoticeModal({ item, onClose }) {
- const isOpen = !!item;
-
- return (
-
{ if (!o) onClose?.(); }} size="5xl" backdrop="blur" className="bg-[#1b1b1b] text-white">
-
- {() => (
-
-
- {item?.title}
-
-
- {/* 이미지: 세로 최대 높이 제한 */}
-
-
- {item?.image && }
-
-
-
- {/* 상세 설명 | 소개글 */}
-
-
- {/* 좌: 상세 설명(요약 카드 + 정보) */}
-
-
상세 설명
-
-
{item?.summary}
-
-
기간 {item?.details?.period || '-'}
-
모집 {item?.details?.recruitment || '-'}
-
일정 {item?.details?.schedule || '-'}
-
장소 {item?.details?.location || '-'}
-
-
-
-
- {/* 우: 소개글(줄글 Markdown) */}
-
-
소개글
- {item?.md ? (
-
-
(
-
- )
- }}
- >
- {item.md}
-
-
- ) : (
-
{item?.summary}
- )}
-
-
-
-
- )}
-
-
- );
-}
-
-
-
-
diff --git a/src/components/recruit/screen/Recruit1.jsx b/src/components/recruit/screen/Recruit1.jsx
deleted file mode 100644
index 0d82fc1..0000000
--- a/src/components/recruit/screen/Recruit1.jsx
+++ /dev/null
@@ -1,76 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { Checkbox } from "@nextui-org/react"
-
-export default function Recruit1({ step, setChecked, updateRecruitData }) {
- const [isAgree, setIsAgree] = useState(false);
-
- useEffect(() => {
- if (step === 1) {
- setChecked(isAgree);
- }
- }, [isAgree, step, setChecked, updateRecruitData]);
-
- return (
-
-
- 아래와 같이 개인정보를 처리하고 제3자에게 제공하는데 동의하십니까?
-
-
-
GDG on Campus INHA 개인정보 수집·이용, 제3자 제공 동의서
-
-
- Google Developer Groups Campus INHA(이하, “이 동아리”라 한다)는 이 동아리의 운영을 위하여 대한민국 「개인정보
- 보호법(법률 제19234호)」에 따라 아래와 같이 개인정보를 수집·이용 및 제3자 제공하고자 합니다. 내용을 자세히
- 읽은 후 동의 여부를 결정하여 주십시오.
-
-
-
✅ 개인정보 수집·이용 및 제3자 제공 항목
-
- 인하대학교 등록 및 졸업·수료·휴학 여부, 성명, 소속 대학(원) 및 전공, 학년, 학번, 전화번호, 이메일, 성별,
- 출생일, 국적, 기타 이 동아리 지원서에 지원자가 제공한 정보
-
-
-
✅ 개인정보 수집·이용 및 제3자 제공 목적
-
1. 이 동아리의 운영 및 관리
-
2. 이 동아리가 참여하는 행사 및 프로그램 운영 및 관리
-
-
✅ 개인정보 제공받는 자 (제3자)
-
구글코리아(유), 정석인하학원(학), 인하대학교 동아리연합회
-
-
✅ 개인정보 보유·이용 및 제3자 제공 기간
-
- 지원서 제출일로부터 1년까지 귀하의 개인정보를 보유 및 이용 그리고 제3자 제공하며, 이후 개인정보는 폐기
- 처리됩니다.
-
-
-
✅ 동의 거부 권리 및 불이익
-
- 귀하께서는 이 안내에 따른 개인정보 수집·이용, 제3자 제공에 대하여 동의를 거부하실 권리가 있습니다. 다만,
- 귀하께서 개인정보 수집·이용, 제3자 제공에 동의하지 않는 경우 이 동아리 지원에 있어 불이익이 발생할 수 있음을
- 알려드립니다.
-
-
-
-
- 모두 동의합니다
-
-
-
- );
-}
diff --git a/src/components/recruit/screen/Recruit10.jsx b/src/components/recruit/screen/Recruit10.jsx
deleted file mode 100644
index f40be7c..0000000
--- a/src/components/recruit/screen/Recruit10.jsx
+++ /dev/null
@@ -1,74 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { Textarea } from '@nextui-org/react';
-
-import SingleSelectBox from '@/components/ui/input/select/SingleSelectBox';
-
-export default function Recruit10({ step, setChecked, updateRecruitData }) {
- const gdgFeedbackOptions = ['저는 신입 멤버입니다', '피드백 사항이 없습니다', 'GDGoC에 원하는 사항이 있습니다'];
- const [gdgFeedback, setGdgFeedback] = useState('');
- const [etcGdgFeedback, setEtcGdgFeedback] = useState(''); //기타 입력 상태
-
- useEffect(() => {
- const isFeedbackFilled = gdgFeedback.trim() !== '';
- const isEtcFeedbackFilled = gdgFeedback !== 'GDGoC에 원하는 사항이 있습니다' || etcGdgFeedback.trim() !== '';
-
- if (step === 10) {
- setChecked(isFeedbackFilled && isEtcFeedbackFilled);
- const formData = {
- gdgFeedback: gdgFeedback === 'GDGoC에 원하는 사항이 있습니다' ? etcGdgFeedback : gdgFeedback,
- };
-
- updateRecruitData(10, formData);
- }
- }, [gdgFeedback, etcGdgFeedback, step, setChecked, updateRecruitData]);
-
- return (
-
-
-
(기존 멤버) 피드백
-
-
-
- • 기존 멤버로 활동하시면서
-
- 건의 및 문의사항 / 아쉬웠던 점 / 바라는 점 / 기억에 남는 재미있었던 활동 / 새로 개설했으면 하는 행사
-
- 등을 자유롭게 작성해주세요.
-
-
-
-
- {gdgFeedback === 'GDGoC에 원하는 사항이 있습니다' && (
-
- )}
-
-
- );
-}
diff --git a/src/components/recruit/screen/Recruit11.jsx b/src/components/recruit/screen/Recruit11.jsx
deleted file mode 100644
index e5fa113..0000000
--- a/src/components/recruit/screen/Recruit11.jsx
+++ /dev/null
@@ -1,80 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { Checkbox } from "@nextui-org/react"
-
-export default function Recruit11({ step, setChecked, updateRecruitData }) {
- const [isPayed, setIsPayed] = useState(false);
-
- useEffect(() => {
- if (step === 11) {
- setChecked(isPayed);
- const formData = {
- isPayed,
- };
- updateRecruitData(11, formData);
- }
- }, [isPayed, step, setChecked, updateRecruitData]);
-
- return (
-
-
-
회비 안내
-
-
- 회비 납부 확인 후, GDG on Campus 멤버로서 모든 활동들에 대한 참가 권한을 얻게 됩니다.
-
-
- 프로젝트와 스터디 등 일부 활동은 운영 소요에 따라 추가금이 산정될 수 있습니다.
-
-
모든 회비는 커뮤니티 운영비로 투명하게 사용, 처리됩니다.
-
-
- • 👛 입금 계좌
-
- : 토스뱅크 1001-9049-2082 | 예금주명
- GDGoC INHA
-
-
- • 💵 25-2 회비
-
- : 20,000 원
-
-
- • 📌 주의사항
-
- : 회비 납부 과정에서 입금자명을 반드시
- [2자리 학번+이름] 으로 변경 해주세요!
-
- → ex) 24김인하
-
-
-
- 문의사항: 박우찬 | 010-2087-1816
-
-
-
-
- 회비 납부를 모두 완료했습니다
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/recruit/screen/Recruit2.jsx b/src/components/recruit/screen/Recruit2.jsx
deleted file mode 100644
index e345c06..0000000
--- a/src/components/recruit/screen/Recruit2.jsx
+++ /dev/null
@@ -1,165 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { Input, Checkbox, Button } from '@nextui-org/react';
-import axios from 'axios';
-
-export default function Recruit2({ step, setChecked, updateRecruitData }) {
- const [name, setName] = useState('');
- const [studentId, setStudentId] = useState('');
- const [enrolledClassification, setEnrolledClassification] = useState('');
- const [isValidStudentId, setIsValidStudentId] = useState(false);
- const [isValidButtonClicked, setIsValidButtonClicked] = useState(false);
-
- useEffect(() => {
- const isNameFilled = name.trim() !== '';
- const isStudentIdFilled = studentId.trim() !== '';
- const isEnrolledClassificationFilled = enrolledClassification !== '';
-
- if (step === 2) {
- setChecked(
- isNameFilled && isStudentIdFilled && isEnrolledClassificationFilled && isValidButtonClicked && isValidStudentId
- );
- const formData = {
- name,
- studentId,
- enrolledClassification,
- };
- updateRecruitData(2, formData);
- }
- }, [
- name,
- studentId,
- enrolledClassification,
- step,
- setChecked,
- updateRecruitData,
- isValidButtonClicked,
- isValidStudentId,
- ]);
-
- const handleCheckDuplicate = async () => {
-
- try {
- const response = await axios.get(`${process.env.NEXT_PUBLIC_BASE_API_URL}/check/student-id`, {
- params: { studentId },
- });
- setIsValidButtonClicked(true);
- setIsValidStudentId(!response.data.data.isExists);
- } catch (error) {
- setIsValidButtonClicked(true);
- console.log('중복확인 오류 발생:', error);
- setIsValidStudentId(false);
- }
- };
-
- return (
-
-
-
필수 개인정보를 적어주세요
-
-
- {
- const numValue = value.replace(/\D/g, '');
- if (numValue.length <= 8) {
- setStudentId(numValue);
- if (isValidButtonClicked) {
- setIsValidButtonClicked(false);
- }
- }
- }}
- variant='bordered'
- autoComplete='off'
- labelPlacement='outside'
- placeholder='8자리 학번을 입력해주세요'
- inputMode='numeric'
- disableAutoFocus
- className='w-60'
- classNames={{
- mainWrapper: 'w-60 h-[57px]',
- label: '!text-white text-xl pb-[18px] mobile:text-lg',
- inputWrapper: `h-[57px] border-[1.5px] rounded-md text-white text-xl mobile:text-lg
- ${
- isValidButtonClicked
- ? isValidStudentId
- ? '!border-[#33A652] group-data-[focus=true]:!border-[#33A652] group-data-[hover=true]:!border-[#33A652]'
- : '!border-[#EA4336] group-data-[focus=true]:!border-[#EA4336] group-data-[hover=true]:!border-[#EA4336]'
- : '!border-[#bbbbbb30] group-data-[focus=true]:!border-[#bbbbbb30] group-data-[hover=true]:!border-[#bbbbbb30]'
- }
- `,
- input: `text-lg mobile:text-base
- ${
- isValidButtonClicked
- ? isValidStudentId
- ? '!text-[#33A652] group-data-[focus=true]:!text-[#33A652] group-data-[hover=true]:!text-[#33A652]'
- : '!text-[#EA4336] group-data-[focus=true]:!text-[#EA4336] group-data-[hover=true]:!text-[#EA4336]'
- : ''
- }
- `,
- errorMessage: 'hidden',
- }}
- />
-
- 중복확인
-
-
- {isValidButtonClicked && (
-
- {isValidStudentId ? '사용이 가능한 학번입니다.' : '이미 등록되었거나 형식에 맞지 않는 학번입니다.'}
-
- )}
-
-
재학 구분
-
- {['정등록', '부분등록', '휴학', '수료', '졸업'].map((label) => (
- setEnrolledClassification(label)}
- radius='none'
- classNames={{
- wrapper: 'hidden',
- label: `text-white text-xl w-[150px] h-[57px] flex justify-center items-center rounded-md mobile:text-base mobile:w-[27vw] mobile:h-[49px]
- ${enrolledClassification === label ? 'bg-[#471915] border-[1.5px] border-[#ea4335]' : 'bg-[#181818]'}`,
- }}
- >
- {label}
-
- ))}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/recruit/screen/Recruit3.jsx b/src/components/recruit/screen/Recruit3.jsx
deleted file mode 100644
index 71acdb5..0000000
--- a/src/components/recruit/screen/Recruit3.jsx
+++ /dev/null
@@ -1,225 +0,0 @@
-"use client";
-
-import { useState, useEffect, useRef } from 'react';
-import { Input, Checkbox, Select, SelectItem, Button } from '@nextui-org/react';
-import axios from 'axios';
-
-export default function Recruit3({ step, setChecked, updateRecruitData }) {
- const [grade, setGrade] = useState('');
-
- const [nationality, setNationality] = useState('');
- const [etcNationality, setEtcNationality] = useState('');
- const nationalityInputRef = useRef(null);
-
- const [phoneNumber, setPhoneNumber] = useState('');
- const [isValidPhoneNumber, setIsValidPhoneNumber] = useState(false);
- const [isValidButtonClicked, setIsValidButtonClicked] = useState(false);
-
- const [isSelectBoxOpen, setIsSelectBoxOpen] = useState(false);
-
- useEffect(() => {
- const isGradeFilled = grade.trim() !== '';
- const isPhoneNumberFilled = phoneNumber.trim() !== '';
- const isNationalityFilled = nationality === '기타' ? etcNationality.trim() !== '' : nationality !== '';
- if (step === 3) {
- setChecked(
- isGradeFilled && isPhoneNumberFilled && isNationalityFilled && isValidPhoneNumber && isValidButtonClicked
- );
- const formData = {
- grade,
- phoneNumber,
- nationality: nationality === '기타' ? etcNationality : nationality,
- };
- updateRecruitData(3, formData);
- }
- }, [
- grade,
- nationality,
- etcNationality,
- phoneNumber,
- step,
- setChecked,
- updateRecruitData,
- isValidPhoneNumber,
- isValidButtonClicked,
- ]);
-
- useEffect(() => {
- if (nationality === '기타' && nationalityInputRef.current) {
- nationalityInputRef.current.focus();
- }
- }, [nationality]);
-
- const handleSelectBoxOpen = (open) => {
- setTimeout(() => {
- setIsSelectBoxOpen(open);
- }, 80);
- };
-
- const handleCheckDuplicate = async () => {
- try {
- const response = await axios.get(`${process.env.NEXT_PUBLIC_BASE_API_URL}/check/phone-number`, {
- params: { phoneNumber },
- });
- setIsValidButtonClicked(true);
- setIsValidPhoneNumber(!response.data.data.isExists);
- } catch (error) {
- setIsValidButtonClicked(true);
- console.log('중복확인 오류 발생:', error);
- setIsValidPhoneNumber(false);
- }
- };
-
- return (
-
-
-
필수 개인정보를 적어주세요
-
setGrade(e.target.value)}
- onOpenChange={handleSelectBoxOpen}
- isOpen={isSelectBoxOpen}
- classNames={{
- trigger:
- 'h-[53px] bg-[#181818] border-[#bbbbbb30] border-[1.5px] data-[hover=true]:bg-[#181818] data-[hover=true]:border-[#bbbbbb30]',
- value: '!text-white text-lg mobile:text-base',
- label: '!text-white text-xl pb-[12px] mobile:text-lg',
- popoverContent: 'bg-[#181818]',
- selectorIcon: 'text-white',
- }}
- >
-
- 1학년
-
-
- 2학년
-
-
- 3학년
-
-
- 4학년
-
-
- 초과학기
-
-
-
- {
- const value = e.target.value.replace(/[^0-9]/g, '');
- if (value.length <= 11) {
- let formattedNumber = '';
- if (value.length > 3) {
- formattedNumber += value.slice(0, 3) + '-';
- if (value.length > 7) {
- formattedNumber += value.slice(3, 7) + '-' + value.slice(7);
- } else {
- formattedNumber += value.slice(3);
- }
- } else {
- formattedNumber = value;
- }
- setPhoneNumber(formattedNumber);
- if (isValidButtonClicked) {
- setIsValidButtonClicked(false);
- }
- }
- }}
- placeholder='010-0000-0000'
- inputMode='numeric'
- variant='bordered'
- labelPlacement='outside'
- className='!mt-[68px] w-60'
- disableAutoFocus
- classNames={{
- mainWrapper: 'w-60 h-[57px]',
- label: '!text-white text-xl pb-[12px] mobile:text-lg',
- inputWrapper: `h-[57px] border-[1.5px] rounded-md text-white text-xl mobile:text-lg
- ${
- isValidButtonClicked
- ? isValidPhoneNumber
- ? '!border-[#33A652] group-data-[focus=true]:!border-[#33A652] group-data-[hover=true]:!border-[#33A652]'
- : '!border-[#EA4336] group-data-[focus=true]:!border-[#EA4336] group-data-[hover=true]:!border-[#EA4336]'
- : '!border-[#bbbbbb30] group-data-[focus=true]:!border-[#bbbbbb30] group-data-[hover=true]:!border-[#bbbbbb30]'
- }`,
- input: `text-lg mobile:text-base
- ${
- isValidButtonClicked
- ? isValidPhoneNumber
- ? '!text-[#33A652] group-data-[focus=true]:!text-[#33A652] group-data-[hover=true]:!text-[#33A652]'
- : '!text-[#EA4336] group-data-[focus=true]:!text-[#EA4336] group-data-[hover=true]:!text-[#EA4336]'
- : ''
- }`,
- }}
- />
-
- 중복확인
-
-
- {isValidButtonClicked && (
-
- {isValidPhoneNumber
- ? '사용이 가능한 전화번호입니다.'
- : '이미 등록되었거나 형식에 맞지 않는 전화번호입니다.'}
-
- )}
-
-
국적
-
- {['대한민국', '기타'].map((label) => (
- setNationality(label)}
- radius='none'
- classNames={{
- wrapper: 'hidden',
- label: `text-white text-lg w-[150px] h-[57px] flex justify-center items-center rounded-md mobile:text-base mobile:w-[27vw] mobile:h-[49px]
- ${nationality === label ? 'bg-[#471915] border-[1.5px] border-[#ea4335]' : 'bg-[#181818]'}`,
- }}
- >
- {label}
-
- ))}
-
-
- {nationality === '기타' && (
-
- )}
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/recruit/screen/Recruit4.jsx b/src/components/recruit/screen/Recruit4.jsx
deleted file mode 100644
index 82b48fa..0000000
--- a/src/components/recruit/screen/Recruit4.jsx
+++ /dev/null
@@ -1,159 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { Input, Checkbox, Select, SelectItem } from '@nextui-org/react';
-
-import { domainOptions } from '@/constant/domainOptions';
-
-export default function Recruit4({ step, setChecked, updateRecruitData }) {
- const [emailId, setEmailId] = useState('');
- const [emailDomain, setEmailDomain] = useState('');
- const [etcEmailDomain, setEtcEmailDomain] = useState('');
- const [gender, setGender] = useState('');
- const [birth, setBirth] = useState('');
- const [isSelectBoxOpen, setIsSelectBoxOpen] = useState(false);
-
- useEffect(() => {
- const isEmailFilled = emailId.trim() !== '';
- const isDomainFilled = emailDomain !== '' && (emailDomain !== 'custom' || etcEmailDomain.trim() !== '');
- const isGenderFilled = gender !== '';
- const isBirthFilled = birth.trim() !== '';
-
- if (step === 4) {
- setChecked(isEmailFilled && isDomainFilled && isGenderFilled && isBirthFilled);
- const formData = {
- email: emailId + '@' + (emailDomain === 'custom' ? etcEmailDomain : emailDomain),
- gender,
- birth,
- };
- updateRecruitData(4, formData);
- }
- }, [emailId, emailDomain, etcEmailDomain, gender, birth, step, setChecked, updateRecruitData]);
-
- const handleSelectBoxOpen = (open) => {
- setTimeout(() => {
- setIsSelectBoxOpen(open);
- }, 80);
- };
-
- return (
-
-
-
필수 개인정보를 적어주세요
-
-
-
- @
- {emailDomain === 'custom' ? (
- {
- setEtcEmailDomain('');
- setEmailDomain('');
- }}
- >
- ×
-
- }
- />
- ) : (
- setEmailDomain(e.target.value)}
- onOpenChange={handleSelectBoxOpen}
- isOpen={isSelectBoxOpen}
- aria-label='이메일'
- className='w-[200px]'
- classNames={{
- trigger:
- 'h-[57px] bg-[#181818] border-[#bbbbbb30] border-[1.5px] data-[hover=true]:bg-[#181818] data-[hover=true]:border-[#bbbbbb30]',
- value: '!text-white text-lg mobile:text-base',
- listbox: 'bg-[#181818] text-white',
- popoverContent: 'bg-[#181818]',
- selectorIcon: 'invert text-white'
- }}
- >
- {domainOptions.map((domain) => (
-
- {domain.label}
-
- ))}
-
- )}
-
-
-
성별
-
- {['남자', '여자'].map((label) => (
- setGender(label)}
- radius='none'
- classNames={{
- wrapper: 'hidden',
- label: `text-white text-lg w-[150px] h-[57px] flex justify-center items-center rounded-md mobile:text-base mobile:w-[27vw] mobile:h-[49px]
- ${gender === label ? 'bg-[#471915] border-[1.5px] border-[#ea4335]' : 'bg-[#181818]'}`,
- }}
- >
- {label}
-
- ))}
-
-
-
setBirth(e.target.value)}
- variant='bordered'
- labelPlacement='outside'
- placeholder='생년월일을 선택해주세요'
- className='!mt-[60px]'
- disableAutoFocus
- classNames={{
- mainWrapper: 'w-60 h-[57px]',
- label: '!text-white text-xl pb-[18px] mobile:text-lg',
- inputWrapper: `h-[57px] border-[#bbbbbb30] border-[1.5px] rounded-md text-white text-xl mobile:text-lg
- group-data-[focus=true]:border-[#bbbbbb30]`,
- input: '!text-black invert text-lg mobile:text-base',
- }}
- />
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/recruit/screen/Recruit5.jsx b/src/components/recruit/screen/Recruit5.jsx
deleted file mode 100644
index 2f09737..0000000
--- a/src/components/recruit/screen/Recruit5.jsx
+++ /dev/null
@@ -1,104 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { Autocomplete, AutocompleteItem, AutocompleteSection, Input } from '@nextui-org/react';
-
-import { majorOptions } from '@/constant/majorOptions';
-
-export default function Recruit5({ step, setChecked, updateRecruitData }) {
- const [major, setMajor] = useState('');
- const [doubleMajor, setDoubleMajor] = useState('');
-
- useEffect(() => {
- const isMajorFilled = major ? major.trim().length : false;
- if (step === 5) {
- setChecked(isMajorFilled);
- const formData = {
- major,
- doubleMajor,
- };
- updateRecruitData(5, formData);
- }
- }, [major, doubleMajor, step, setChecked, updateRecruitData]);
-
- const handlePreventEnter = (event) => {
- if (event.key === 'Enter') {
- event.preventDefault();
- }
- };
-
- return (
-
-
필수 개인정보를 적어주세요
-
- {majorOptions.map((major) => (
-
- {major.items.map((item) => (
-
- {item.value}
-
- ))}
-
- ))}
-
-
-
다중전공 (선택)
-
- • 현재 진행 중인 다중 전공(복수전공, 부전공, 융합전공, 연계전공)을 순서에 맞게 띄어쓰기 없이 정확한 이름으로
- 입력해주세요.
- ex) XXX 학과 복수전공, 000학과 융합전공
-
-
-
-
- );
-}
diff --git a/src/components/recruit/screen/Recruit6.jsx b/src/components/recruit/screen/Recruit6.jsx
deleted file mode 100644
index d1dccb1..0000000
--- a/src/components/recruit/screen/Recruit6.jsx
+++ /dev/null
@@ -1,51 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { Textarea } from '@nextui-org/react';
-
-export default function Recruit6({ step, setChecked, updateRecruitData }) {
- const [gdgUserMotive, setGdgUserMotive] = useState('');
-
- useEffect(() => {
- if (step === 6) {
- setChecked(gdgUserMotive.trim() !== '');
- const formData = {
- gdgUserMotive,
- };
-
- updateRecruitData(6, formData);
- }
- }, [gdgUserMotive, step, setChecked, updateRecruitData]);
-
- return (
-
-
-
지원 동기
-
-
- • GDG on Campus INHA 멤버 가입을 지원하게 된 동기가 어떻게 되시나요?
- • 많은 동아리들 중, 'GDG on Campus INHA' 를 선택하신 계기가 궁금합니다.
- • 글자 수 제한은 없습니다. 편하게 작성 부탁드립니다.
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/recruit/screen/Recruit7.jsx b/src/components/recruit/screen/Recruit7.jsx
deleted file mode 100644
index 8a1113d..0000000
--- a/src/components/recruit/screen/Recruit7.jsx
+++ /dev/null
@@ -1,56 +0,0 @@
-"use client";
-
-import { useState, useEffect } from 'react';
-import { Textarea } from '@nextui-org/react';
-
-export default function Recruit7({ step, setChecked, updateRecruitData }) {
- const [gdgUserStory, setGdgUserStory] = useState('');
-
- useEffect(() => {
- if (step === 7) {
- setChecked(true);
- const formData = {
- gdgUserStory,
- };
-
- updateRecruitData(7, formData);
- }
- }, [gdgUserStory, step, setChecked, updateRecruitData]);
-
- return (
-
-
-
지원자의 삶과 진로 경험 & 이야기 (선택)
-
-
-
- • GDG on campus INHA 25-2 가입 이전, 어떤 활동 및 공부를 하고 계셨나요? 여러분에 대한 정보를 편하게
- 알려주세요! 기존 멤버분들께서는 간단하게 적어주셔도 됩니다 😀
-
-
- • ex) 1학년: ☆☆동아리 하면서 대학생활 / 2학년: ☆☆을 공부하는 학부 연구생, ☆☆대외활동 / 3학년: 잠시의
- 방황과 교환학생 / 4학년: ☆☆기업 인턴
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/recruit/screen/Recruit8.jsx b/src/components/recruit/screen/Recruit8.jsx
deleted file mode 100644
index 2bb6778..0000000
--- a/src/components/recruit/screen/Recruit8.jsx
+++ /dev/null
@@ -1,105 +0,0 @@
-import { useState, useEffect, useRef } from 'react';
-import { Input } from '@nextui-org/react';
-
-import MultipleSelectBox from '@/components/ui/input/select/MultipleSelectBox';
-import SingleSelectBox from '@/components/ui/input/select/SingleSelectBox';
-
-import { interestOptions } from '@/constant/interestOptions';
-import { reachFromOptions } from "@/constant/reachFromOptions";
-import { semesterOptions } from '@/constant/semesterOptions';
-
-export default function Recruit8({ step, setChecked, updateRecruitData }) {
- const [gdgInterest, setGdgInterest] = useState([]);
- const [gdgPeriod, setGdgPeriod] = useState([]);
- const [gdgRoute, setGdgRoute] = useState('');
- const [etcGdgRoute, setEtcGdgRoute] = useState('');
- const gdgRouteInputRef = useRef(null);
-
- useEffect(() => {
- const isInterestFilled = gdgInterest.length > 0;
- const isPeriodFilled = gdgPeriod.length > 0;
- const isRouteFilled = gdgRoute.trim() !== '';
- const isEtcRouteFilled = gdgRoute !== '기타' || etcGdgRoute.trim() !== '';
-
- if (step === 8) {
- setChecked(isInterestFilled && isPeriodFilled && isRouteFilled && isEtcRouteFilled);
- const formData = {
- gdgInterest,
- gdgPeriod,
- gdgRoute: gdgRoute === '기타' ? etcGdgRoute : gdgRoute,
- };
-
- updateRecruitData(8, formData);
- }
- }, [gdgInterest, gdgPeriod, gdgRoute, etcGdgRoute, step, setChecked, updateRecruitData]);
-
- useEffect(() => {
- if (gdgRoute === '기타' && gdgRouteInputRef.current) {
- gdgRouteInputRef.current.focus();
- }
- }, [gdgRoute]);
-
- return (
-
-
-
-
GDG on Campus 기수
-
-
-
- • 현재 지원 기수 및 기존 활동 기수를 모두 선택해주세요.
-
- • 신입 멤버는 '25-2' 하나만 선택해주시면 됩니다.
-
-
-
-
- 어떤 경로를 통해 GDG on Campus를 알게 되셨나요?
-
-
- {gdgRoute === '기타' && (
-
- )}
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/recruit/screen/Recruit9.jsx b/src/components/recruit/screen/Recruit9.jsx
deleted file mode 100644
index dbada1e..0000000
--- a/src/components/recruit/screen/Recruit9.jsx
+++ /dev/null
@@ -1,120 +0,0 @@
-'use client';
-
-import { useState, useEffect } from 'react';
-import { Input } from '@nextui-org/react';
-
-import MultipleSelectBox from '@/components/ui/input/select/MultipleSelectBox';
-
-import { wishOptions } from '@/constant/wishOptions';
-import { expectOptions } from '@/constant/expectOptions';
-
-export default function Recruit9({ step, setChecked, updateRecruitData }) {
- const [gdgWish, setGdgWish] = useState([]);
- const [etcGdgWish, setEtcGdgWish] = useState(''); //기타 입력 상태
- const [gdgExpect, setGdgExpect] = useState([]);
- const [etcGdgExpect, setEtcGdgExpect] = useState(''); //기타 입력 상태
-
- useEffect(() => {
- const isWishFilled = gdgWish.length > 0;
- const isEtcWishFilled = !gdgWish.includes('기타') || etcGdgWish.trim() !== '';
-
- const isExpectFilled = gdgExpect.length > 0;
- const isEtcExpectFilled = !gdgExpect.includes('기타') || etcGdgExpect.trim() !== '';
-
- if (step === 9) {
- setChecked(isWishFilled && isEtcWishFilled && isExpectFilled && isEtcExpectFilled);
-
- const gdgWishCombined = gdgWish
- .map((wish) => (wish === '기타' ? etcGdgWish : wish))
- .filter((wish) => wish !== '기타' || etcGdgWish.trim() !== '');
- const gdgExpectCombined = gdgExpect
- .map((expect) => (expect === '기타' ? etcGdgExpect : expect))
- .filter((expect) => expect !== '기타' || etcGdgExpect.trim() !== '');
-
- const formData = {
- gdgWish: gdgWishCombined,
- gdgExpect: gdgExpectCombined,
- };
-
- updateRecruitData(9, formData);
- }
- }, [gdgWish, etcGdgWish, gdgExpect, etcGdgExpect, step, setChecked, updateRecruitData]);
-
- return (
-
-
-
- GDG on Campus INHA에서 무엇을 얻어가고 싶으신가요? (중복선택 가능)
-
-
-
-
- {gdgWish.includes('기타') && (
-
setEtcGdgWish(e.target.value)}
- classNames={{
- mainWrapper: 'w-[500px] h-[57px] mobile:w-[85vw]',
- label: '!text-white text-lg pb-[18px] mobile:text-base',
- inputWrapper: `h-[57px] border-[#bbbbbb30] border-[1.5px] rounded-md text-white text-xl mobile:text-base
- group-data-[focus=true]:border-[#bbbbbb30]`,
- input: 'text-lg mobile:text-base',
- }}
- />
- )}
-
-
- 이 외에 GDG on campus에서 기대하거나 원하는 활동이 있으신가요?
-
-
-
- • ex) 이런 프로젝트 하고 싶어요, 이런 스터디 있으면 좋겠어요, 이런 활동 있으면 좋겠어요
-
- • 선택지에 없는 활동을 원하시면 '기타' 항목을 선택
- 후 요청사항 을 작성해주세요. 반영하여 활동을 기획하겠습니다!
-
-
-
-
-
- {gdgExpect.includes('기타') && (
-
setEtcGdgExpect(e.target.value)}
- classNames={{
- mainWrapper: 'w-[500px] h-[57px] mobile:w-[85vw]',
- label: '!text-white text-xl pb-[18px] mobile:text-lg',
- inputWrapper: `h-[57px] border-[#bbbbbb30] border-[1.5px] rounded-md text-white text-xl mobile:text-lg
- group-data-[focus=true]:border-[#bbbbbb30]`,
- input: 'text-lg mobile:text-base',
- }}
- />
- )}
-
-
- );
-}
diff --git a/src/components/study/dashboard/admin/detail/ReviewApplication.jsx b/src/components/study/dashboard/admin/detail/ReviewApplication.jsx
index 90848b7..f1c1a5f 100644
--- a/src/components/study/dashboard/admin/detail/ReviewApplication.jsx
+++ b/src/components/study/dashboard/admin/detail/ReviewApplication.jsx
@@ -13,7 +13,6 @@ import { useApplicantList } from "@/services/study/useApplicantList";
import { useStudyAccessCheck } from "@/services/study/useStudyAccessCheck";
// components
-import NoticeBanner from "@/components/study/ui/card/NoticeBanner";
import ApplicantInfoList from "@/components/study/ui/card/ApplicantInfoList";
import ApplicantDetailModal from "@/components/study/ui/modal/ApplicantDetailModal";
@@ -300,7 +299,6 @@ export default function ReviewApplication({ studyId }) {
)}
{isStudyLead ? (
-
{
if (status === 'APPROVED')
- return "bg-blue-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-blue-400 text-white px-3 py-1 rounded-full text-sm font-medium";
if (status === 'REJECTED')
- return "bg-red-500 text-white px-3 py-1 rounded-full text-sm font-medium";
- return "bg-yellow-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-red-400 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-yellow-400 text-white px-3 py-1 rounded-full text-sm font-medium";
};
// Status text
@@ -77,7 +77,7 @@ export default function CreatedStudies() {
e.stopPropagation();
handleDetailClick(data.id);
}}
- className="text-blue-500 hover:text-blue-700 text-sm"
+ className="text-blue-400 hover:text-blue-700 text-sm"
>
상세 보기
@@ -86,7 +86,7 @@ export default function CreatedStudies() {
))
) : (
-
+
데이터가 없습니다.
diff --git a/src/components/study/dashboard/my/detail/StudyDetail.jsx b/src/components/study/dashboard/my/detail/StudyDetail.jsx
index dc28988..0d8eb80 100644
--- a/src/components/study/dashboard/my/detail/StudyDetail.jsx
+++ b/src/components/study/dashboard/my/detail/StudyDetail.jsx
@@ -36,7 +36,7 @@ export default function StudyDetail({ studyId }) {
현재 기능 준비 중인 페이지입니다.
>
) : (
-
+
접근 권한이 없는 스터디입니다.
)}
diff --git a/src/components/study/dashboard/my/detail/futureRef/Attendance.jsx b/src/components/study/dashboard/my/detail/futureRef/Attendance.jsx
index 54e96f0..b0bc577 100644
--- a/src/components/study/dashboard/my/detail/futureRef/Attendance.jsx
+++ b/src/components/study/dashboard/my/detail/futureRef/Attendance.jsx
@@ -19,11 +19,11 @@ export default function Attendance() {
const baseClasses = "px-3 py-1 rounded-full text-sm font-medium text-white";
if (status === 'ATTEND')
- return `${baseClasses} bg-green-500`;
+ return `${baseClasses} bg-green-400`;
if (status === 'LATE')
- return `${baseClasses} bg-yellow-500`;
+ return `${baseClasses} bg-yellow-400`;
if (status === 'ABSENT')
- return `${baseClasses} bg-red-500`;
+ return `${baseClasses} bg-red-400`;
return `${baseClasses} bg-blue-700`;
};
@@ -35,19 +35,19 @@ export default function Attendance() {
diff --git a/src/components/study/dashboard/my/detail/futureRef/WeeklyInfo.jsx b/src/components/study/dashboard/my/detail/futureRef/WeeklyInfo.jsx
index 5382d01..002b08f 100644
--- a/src/components/study/dashboard/my/detail/futureRef/WeeklyInfo.jsx
+++ b/src/components/study/dashboard/my/detail/futureRef/WeeklyInfo.jsx
@@ -56,14 +56,14 @@ export default function WeeklyInfo() {
{data.weeklyRecords.map((week) => (
-
+
{week.description}
diff --git a/src/components/study/dashboard/my/view/AppliedStudies.jsx b/src/components/study/dashboard/my/view/AppliedStudies.jsx
index 3d8308c..dc5c7d3 100644
--- a/src/components/study/dashboard/my/view/AppliedStudies.jsx
+++ b/src/components/study/dashboard/my/view/AppliedStudies.jsx
@@ -23,10 +23,10 @@ export default function AppliedStudies() {
const getStatusBadge = (status) => {
if (status === 'APPROVED')
- return "bg-blue-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-blue-400 text-white px-3 py-1 rounded-full text-sm font-medium";
if (status === 'REJECTED')
- return "bg-red-500 text-white px-3 py-1 rounded-full text-sm font-medium";
- return "bg-yellow-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-red-400 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-yellow-400 text-white px-3 py-1 rounded-full text-sm font-medium";
};
const getStatusName = (status) => {
@@ -78,7 +78,7 @@ export default function AppliedStudies() {
e.stopPropagation();
handleDetailClick(data.studyId);
}}
- className="text-blue-500 hover:text-blue-700 text-sm"
+ className="text-blue-400 hover:text-blue-700 text-sm"
>
정보 보기
@@ -87,7 +87,7 @@ export default function AppliedStudies() {
))
) : (
-
+
데이터가 없습니다.
diff --git a/src/components/study/ui/card/ApplicantInfoList.jsx b/src/components/study/ui/card/ApplicantInfoList.jsx
index f8c526a..906c44e 100644
--- a/src/components/study/ui/card/ApplicantInfoList.jsx
+++ b/src/components/study/ui/card/ApplicantInfoList.jsx
@@ -16,9 +16,9 @@ export default function ApplicantInfoList({
// Status badge styles
const getStatusBadge = (status) => {
if (status === 'APPROVED')
- return "bg-blue-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-blue-400 text-white px-3 py-1 rounded-full text-sm font-medium";
if (status === 'REJECTED')
- return "bg-red-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-red-400 text-white px-3 py-1 rounded-full text-sm font-medium";
return "bg-gray-300 text-gray-700 px-3 py-1 rounded-full text-sm font-medium";
};
@@ -45,7 +45,7 @@ export default function ApplicantInfoList({
{applications?.length === 0 ? (
-
+
아직 지원자가 없습니다. ㅠㅠ
@@ -92,12 +92,12 @@ export default function ApplicantInfoList({
handleClick={handleApproval}
/>
{isApprovalButtonDisabled && hasProcessedApplicants && (
-
+
이미 처리된 지원자가 있어 수정할 수 없습니다.
)}
{isApprovalButtonDisabled && !hasProcessedApplicants && (
-
+
지원 마감일이 지나 자동으로 처리되었습니다.
)}
diff --git a/src/components/study/ui/card/NoticeBanner.jsx b/src/components/study/ui/card/NoticeBanner.jsx
deleted file mode 100644
index d85b4cf..0000000
--- a/src/components/study/ui/card/NoticeBanner.jsx
+++ /dev/null
@@ -1,20 +0,0 @@
-'use client';
-
-export default function NoticeBanner() {
- return (
-
-
-
💡
-
-
모집 관련 주의사항
-
- • 모집 마감일 다음 날까지는 자유롭게 합/불 체크가 가능합니다.
- • 마감일 이전에는 최종 결정 및 마감 버튼으로 조기 마감이 가능합니다.
- • 모집 마감일 하루 뒤 자정에는 자동으로 마지막으로 체크한 합/불 결과가 반영됩니다.
- • 지원자는 MY 스터디 참여 현황에서 모집 마감 시간 하루 뒤 합격여부 조회가 가능합니다.
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/src/components/study/ui/card/StudyCard.jsx b/src/components/study/ui/card/StudyCard.jsx
index 078d517..88a0b67 100644
--- a/src/components/study/ui/card/StudyCard.jsx
+++ b/src/components/study/ui/card/StudyCard.jsx
@@ -12,13 +12,13 @@ export default function StudyCard({ id, title, description, status, reqEnd, icon
const getStatusStyle = (status) => {
switch (status.toUpperCase()) {
case 'RECRUITED':
- return 'bg-red-500 text-white';
+ return 'bg-red-400 text-white';
case 'RECRUITING':
- return 'bg-green-500 text-white';
+ return 'bg-green-400 text-white';
case 'CANCELLED':
- return 'bg-yellow-500 text-white';
+ return 'bg-yellow-400 text-white';
default:
- return 'bg-blue-500 text-white';
+ return 'bg-blue-400 text-white';
}
};
@@ -49,7 +49,7 @@ export default function StudyCard({ id, title, description, status, reqEnd, icon
{ title }
{ description }
-
모집마감: {reqEnd ? formatDate(reqEnd) : '정보없음'}
+
모집마감: {reqEnd ? formatDate(reqEnd) : '정보없음'}
diff --git a/src/components/study/ui/modal/ApplicantDetailModal.jsx b/src/components/study/ui/modal/ApplicantDetailModal.jsx
index 7f6038c..f5bc167 100644
--- a/src/components/study/ui/modal/ApplicantDetailModal.jsx
+++ b/src/components/study/ui/modal/ApplicantDetailModal.jsx
@@ -19,9 +19,9 @@ export default function ApplicantDetailModal({ apiClient, studyId, selectedAppli
// Status badge styles
const getStatusBadge = (status) => {
if (status === 'APPROVED')
- return "bg-blue-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-blue-400 text-white px-3 py-1 rounded-full text-sm font-medium";
if (status === 'REJECTED')
- return "bg-red-500 text-white px-3 py-1 rounded-full text-sm font-medium";
+ return "bg-red-400 text-white px-3 py-1 rounded-full text-sm font-medium";
return "bg-gray-300 text-gray-700 px-3 py-1 rounded-full text-sm font-medium";
};
diff --git a/src/components/study/ui/nav/StudyDashboardNav.jsx b/src/components/study/ui/nav/StudyDashboardNav.jsx
index 729fad9..bdb3592 100644
--- a/src/components/study/ui/nav/StudyDashboardNav.jsx
+++ b/src/components/study/ui/nav/StudyDashboardNav.jsx
@@ -44,7 +44,7 @@ export default function StudyDashboardNav({ isAdminPage = false, studyId = null,
{/* Mobile */}
{menuOption.length > 0 && (
handleMenuClick(currentMenu)}
>
{menuOption.find(option => option.id === currentMenu).label}
@@ -77,7 +77,7 @@ export default function StudyDashboardNav({ isAdminPage = false, studyId = null,
onClick={() => handleMenuClick(item.id)}
className={`py-1 px-3 text-sm cursor-pointer transition-colors duration-200 rounded-full
${currentMenu === item.id
- ? 'bg-blue-600 text-white'
+ ? 'bg-blue-400 text-white'
: 'text-white hover:bg-gray-800'
}`}
>
@@ -95,7 +95,7 @@ export default function StudyDashboardNav({ isAdminPage = false, studyId = null,
onClick={() => handleMenuClick(item.id)}
className={`py-2 text-sm cursor-pointer transition-colors duration-200
${currentMenu === item.id
- ? 'text-blue-500 font-medium'
+ ? 'text-blue-400 font-medium'
: 'text-white hover:text-blue-300'
}`}
>
diff --git a/src/components/ui/button/GreenTextButton.jsx b/src/components/ui/button/GreenTextButton.tsx
similarity index 71%
rename from src/components/ui/button/GreenTextButton.jsx
rename to src/components/ui/button/GreenTextButton.tsx
index 842d2de..29a22aa 100644
--- a/src/components/ui/button/GreenTextButton.jsx
+++ b/src/components/ui/button/GreenTextButton.tsx
@@ -3,7 +3,7 @@ export default function GreenTextButton({ text, isDisabled, handleClick }) {
{text}
diff --git a/src/components/ui/button/RoundImageButton.jsx b/src/components/ui/button/RoundImageButton.tsx
similarity index 100%
rename from src/components/ui/button/RoundImageButton.jsx
rename to src/components/ui/button/RoundImageButton.tsx
diff --git a/src/components/ui/button/SubmitButton.jsx b/src/components/ui/button/SubmitButton.tsx
similarity index 87%
rename from src/components/ui/button/SubmitButton.jsx
rename to src/components/ui/button/SubmitButton.tsx
index 6e17d26..eb08716 100644
--- a/src/components/ui/button/SubmitButton.jsx
+++ b/src/components/ui/button/SubmitButton.tsx
@@ -6,7 +6,7 @@ export default function SubmitButton({ type, text, isDisabled, handleClick }) {
{text}
diff --git a/src/components/ui/common/DevHeader.jsx b/src/components/ui/common/DevHeader.tsx
similarity index 100%
rename from src/components/ui/common/DevHeader.jsx
rename to src/components/ui/common/DevHeader.tsx
diff --git a/src/components/ui/common/Header.jsx b/src/components/ui/common/Header.jsx
deleted file mode 100644
index e49708f..0000000
--- a/src/components/ui/common/Header.jsx
+++ /dev/null
@@ -1,25 +0,0 @@
-import React from 'react';
-import NextImage from 'next/image';
-
-import gdgocIcon from "@public/icons/logo.png";
-
-function Header() {
- return (
-
-
-
-
- GDGoC Inha univ.
-
-
-
- );
-}
-
-export default Header;
\ No newline at end of file
diff --git a/src/components/ui/common/Header.tsx b/src/components/ui/common/Header.tsx
new file mode 100644
index 0000000..7a93dd1
--- /dev/null
+++ b/src/components/ui/common/Header.tsx
@@ -0,0 +1,14 @@
+import React from 'react'
+import { GdgLogo } from '@/components/ui/design-system'
+
+function Header() {
+ return (
+
+ )
+}
+
+export default Header
diff --git a/src/components/ui/common/Header2.jsx b/src/components/ui/common/Header2.jsx
deleted file mode 100644
index 6b97565..0000000
--- a/src/components/ui/common/Header2.jsx
+++ /dev/null
@@ -1,17 +0,0 @@
-import React from 'react';
-import Image from 'next/image';
-
-import gdgocIcon from '@public/icons/logo.png';
-
-export default function Header2() {
- return (
-
-
-
-
- GDGoC Inha univ.
-
-
-
- );
-}
diff --git a/src/components/ui/common/Header2.tsx b/src/components/ui/common/Header2.tsx
new file mode 100644
index 0000000..2a358c5
--- /dev/null
+++ b/src/components/ui/common/Header2.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import { GdgLogo } from '@/components/ui/design-system';
+
+export default function Header2() {
+ return (
+
+ );
+}
diff --git a/src/components/ui/common/Loader.jsx b/src/components/ui/common/Loader.jsx
deleted file mode 100644
index 3d9b160..0000000
--- a/src/components/ui/common/Loader.jsx
+++ /dev/null
@@ -1,11 +0,0 @@
-const Loader = ({ isLoading }) => {
- if (!isLoading) return null;
-
- return (
-
- );
- };
-
- export default Loader;
\ No newline at end of file
diff --git a/src/components/ui/common/Loader.tsx b/src/components/ui/common/Loader.tsx
new file mode 100644
index 0000000..a585b6b
--- /dev/null
+++ b/src/components/ui/common/Loader.tsx
@@ -0,0 +1,15 @@
+type LoaderProps = {
+ isLoading?: boolean;
+};
+
+const Loader = ({ isLoading = true }: LoaderProps) => {
+ if (!isLoading) return null;
+
+ return (
+
+ );
+ };
+
+ export default Loader;
diff --git a/src/components/ui/common/MenuHeader.jsx b/src/components/ui/common/MenuHeader.tsx
similarity index 99%
rename from src/components/ui/common/MenuHeader.jsx
rename to src/components/ui/common/MenuHeader.tsx
index dcb40fe..8dcf806 100644
--- a/src/components/ui/common/MenuHeader.jsx
+++ b/src/components/ui/common/MenuHeader.tsx
@@ -5,7 +5,7 @@ import { Navbar, NavbarBrand, NavbarContent, NavbarItem, NavbarMenuToggle, Navba
import { Heart, User, LogOut } from "lucide-react";
import Image from 'next/image';
-import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi.js';
+import { useAuthenticatedApi } from '@/hooks/useAuthenticatedApi';
import gdgocIcon from '@public/icons/logo.png';
@@ -92,7 +92,7 @@ export default function MenuHeader() {
{menuItems.map((item, index) => (
= {
+ signup: [
+ {
+ category: '회원가입',
+ purpose: ['본인 확인 및 부원 식별', '서비스 제공 및 공지사항 전달'],
+ fields: '이름, 학과, 학번, 휴대전화번호',
+ retention: '서비스 탈퇴 시까지'
+ }
+ ],
+ member: [
+ {
+ category: '부원 모집',
+ purpose: ['선발 전형 진행 및 결과 안내', '성별/전공 비율 통계 분석'],
+ fields: '이름, 성별, 생년월일, 주전공, 재학상태, 학번, 휴대전화번호, 이메일',
+ retention: '동아리 탈퇴 시까지'
+ },
+ {
+ category: '군 휴학 증빙',
+ purpose: ['군 휴학 부원 회비 면제 대상 확인'],
+ fields: '복무 확인 서류 (입영통지서, 복무증명서 등)',
+ retention: '확인 후 5일 이내 파기'
+ }
+ ],
+ core: [
+ {
+ category: '운영진 모집',
+ purpose: ['선발 전형 진행 및 결과 안내', '선발 후 명부 관리 및 권한 부여'],
+ fields: '이름, 학번, 이메일, 주전공, 휴대전화번호',
+ retention: '임기 종료 시까지 (미선발 시 5일 이내 파기)'
+ }
+ ]
+}
+
+function RowBlock({ row, compact }: { row: PolicyRow; compact?: boolean }) {
+ const rowTitleTypo = compact ? 'typo-pc-b2 mobile:typo-m-b3' : 'typo-pc-s2 mobile:typo-m-s1'
+ const rowBodyTypo = compact ? 'typo-pc-c1 mobile:typo-m-c2' : 'typo-pc-b2 mobile:typo-m-b1'
+
+ return (
+
+
{row.category}
+
+
+ 처리 목적 {row.purpose.join(' / ')}
+
+
+ 수집 항목 {row.fields}
+
+
+ 보유 기간 {row.retention}
+
+
+
+ )
+}
+
+export function PrivacyPolicyNotice({
+ target,
+ title = '개인정보 처리방침',
+ showTitle = true,
+ compact = false,
+ className
+}: {
+ target: PolicyTarget
+ title?: string
+ showTitle?: boolean
+ compact?: boolean
+ className?: string
+}) {
+ const rows = POLICY_ROWS[target]
+ const compactMetaTypo = compact ? 'typo-pc-c1 mobile:typo-m-c2' : 'typo-pc-c1 mobile:typo-m-c1'
+
+ return (
+
+ {showTitle ?
{title}
: null}
+
+
+ GDGoC INHA는 「개인정보 보호법」 등 관련 법령에 따라 개인정보를 안전하게 처리합니다.
+
+
+ {rows.map((row) => (
+
+ ))}
+
+
+
+ 보유기간 종료, 탈퇴, 미선발 등의 사유 발생 시 영업일 기준 5일 이내 파기합니다.
+
+
+ 파기 시 디지털 데이터는 복구 불가능한 방식으로 삭제하고, 문서는 세절 처리합니다.
+
+
+
+
+ )
+}
diff --git a/src/components/ui/design-system/DesignSystemShowcase.tsx b/src/components/ui/design-system/DesignSystemShowcase.tsx
new file mode 100644
index 0000000..93bff23
--- /dev/null
+++ b/src/components/ui/design-system/DesignSystemShowcase.tsx
@@ -0,0 +1,600 @@
+'use client'
+
+import React, { useState } from 'react'
+import { GdgButton, GDG_BUTTON_SIZES, GDG_BUTTON_VARIANTS } from './GdgButton'
+import { GdgCheckbox, GDG_CHECKBOX_SIZES } from './GdgCheckbox'
+import {
+ GdgColorTag,
+ GDG_COLOR_TAG_COLORS,
+ GDG_COLOR_TAG_FILLS,
+ GDG_COLOR_TAG_SIZES
+} from './GdgColorTag'
+import { GdgDropdown, type GdgDropdownSize } from './GdgDropdown'
+import {
+ GdgFileCard,
+ GdgUploadButton,
+ GDG_FILE_CARD_ACTIONS,
+ GDG_FILE_CARD_DEVICES
+} from './GdgFileCard'
+import { GdgInputField, GDG_INPUT_STATES } from './GdgInputField'
+import { GdgFieldContainer } from './GdgFieldContainer'
+import { GdgLogo, GDG_LOGO_MODES, GDG_LOGO_VARIANTS } from './GdgLogo'
+import { GdgGoogleLoginButton } from './GdgGoogleLoginButton'
+import { GdgMajorDropdown } from './GdgMajorDropdown'
+import { GdgRadio } from './GdgRadio'
+import { GdgSearchField, GDG_SEARCH_FIELD_WIDTHS } from './GdgSearchField'
+import {
+ GdgSegmentedButton,
+ GDG_SEGMENTED_BUTTON_DEVICES,
+ GDG_SEGMENTED_BUTTON_EDGES
+} from './GdgSegmentedButton'
+import { GdgTag, GDG_TAG_DEVICES, GDG_TAG_VARIANTS } from './GdgTag'
+import { GdgTextarea, GDG_TEXTAREA_DEVICES, GDG_TEXTAREA_STATES } from './GdgTextarea'
+import { GDG_MOBILE_WIDTHS, GDG_PC_NARROW_WIDTHS, GDG_PC_WIDE_WIDTHS } from './controlMeta'
+
+const INPUT_DENSITIES = ['default', 'mini'] as const
+
+const DROPDOWN_PC_WIDE_SIZES: GdgDropdownSize[] = ['mini', 'small', 'medium', 'full']
+const DROPDOWN_MOBILE_SIZES: GdgDropdownSize[] = ['small', 'medium', 'twoThirds', 'full']
+
+const dropdownOptions = [
+ { id: 'opt-1', label: '옵션 1' },
+ { id: 'opt-2', label: '옵션 2' },
+ { id: 'opt-3', label: '옵션 3' }
+]
+
+const groupedDropdownOptions = [
+ {
+ title: '공과대학',
+ items: [
+ { id: 'cse', label: '컴퓨터공학과' },
+ { id: 'ai', label: '인공지능공학과' }
+ ]
+ },
+ {
+ title: '자연과학대학',
+ items: [
+ { id: 'math', label: '수학과' },
+ { id: 'stat', label: '통계학과' }
+ ]
+ }
+]
+
+function Section({ title, children }: { title: string; children: React.ReactNode }) {
+ return (
+
+ )
+}
+
+export function DesignSystemShowcase() {
+ const [majorPc, setMajorPc] = useState('')
+ const [majorMobile, setMajorMobile] = useState('')
+ const [buttonState, setButtonState] = useState<
+ Record<'pc' | 'mobile', { active: boolean; clicks: number }>
+ >({
+ pc: { active: false, clicks: 0 },
+ mobile: { active: false, clicks: 0 }
+ })
+ const [segmentedPressed, setSegmentedPressed] = useState<
+ Record<'pc' | 'mobile', 'left' | 'right'>
+ >({
+ pc: 'left',
+ mobile: 'left'
+ })
+ const [checkboxChecked, setCheckboxChecked] = useState>({
+ pc: false,
+ mobile: false
+ })
+ const [radioChecked, setRadioChecked] = useState>({
+ pc: 'left',
+ mobile: 'left'
+ })
+
+ return (
+
+
Design System Showcase
+
+
+ {GDG_LOGO_MODES.map((mode) =>
+ GDG_LOGO_VARIANTS.map((variant) => (
+
+
{`${mode} / ${variant}`}
+
+
+ ))
+ )}
+
+
+
+
+
+
+ {GDG_SEGMENTED_BUTTON_DEVICES.map((device) => (
+
+
{`${device} / interactive`}
+
+
+ setButtonState((prev) => ({
+ ...prev,
+ [device]: {
+ active: !prev[device].active,
+ clicks: prev[device].clicks + 1
+ }
+ }))
+ }
+ >
+ {buttonState[device].active ? 'ACTIVE' : 'DEFAULT'}
+
+
{`click: ${buttonState[device].clicks}`}
+
+
+ ))}
+
+
+ {GDG_SEGMENTED_BUTTON_DEVICES.map((device) =>
+ GDG_BUTTON_SIZES.map((size) => (
+
+
{`${device} / ${size}`}
+
+ {GDG_BUTTON_VARIANTS.filter((v) => v !== 'white').map((variant) => (
+
+
+ {variant.toUpperCase()}
+
+ {variant}
+
+ ))}
+
+
+ ))
+ )}
+
+
+
+
+ Width Tokens (PC Narrow)
+
+
+ {GDG_PC_NARROW_WIDTHS.map((width) => (
+
+
{width}
+
+ {width}
+
+
+ ))}
+
+
+
+
+
+ Width Tokens (PC Wide)
+
+
+ {GDG_PC_WIDE_WIDTHS.map((width) => (
+
+
{width}
+
+ {width}
+
+
+ ))}
+
+
+
+
+
+ Width Tokens (Mobile)
+
+
+ {GDG_MOBILE_WIDTHS.map((width) => (
+
+
{width}
+
+ {width}
+
+
+ ))}
+
+
+
+
+
+ Specialized: Google Login Button
+
+
+
+
+
Mobile Style (260x46)
+
+
+
+
+
+
+
+
+
+ {GDG_SEGMENTED_BUTTON_DEVICES.map((device) => (
+
+
{device}
+
+ {GDG_SEGMENTED_BUTTON_EDGES.map((edge) => (
+ setSegmentedPressed((prev) => ({ ...prev, [device]: edge }))}
+ >
+ {edge.charAt(0).toUpperCase() + edge.slice(1)}
+
+ ))}
+
+
{`selected: ${segmentedPressed[device]}`}
+
+ {GDG_SEGMENTED_BUTTON_EDGES.map((edge) => (
+
+ Disabled
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+ {GDG_TAG_DEVICES.map((device) => (
+
+
{device}
+
+ {GDG_TAG_VARIANTS.map((variant) => (
+
+ {variant}
+
+ ))}
+
+
+ ))}
+
+
color tag
+
+ {GDG_COLOR_TAG_COLORS.map((color) => (
+
+
{color}
+
+ {GDG_COLOR_TAG_SIZES.map((size) => (
+
+ {GDG_COLOR_TAG_FILLS.map((fill) => (
+
+ {`${size}-${fill}`}
+
+ ))}
+
+ ))}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
Field Container States
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
pc / narrow widths
+
+ {GDG_PC_NARROW_WIDTHS.map((width) =>
+ GDG_INPUT_STATES.map((state) => (
+
+ ))
+ )}
+
+
+
+
pc / wide widths (mini 포함)
+
+ {GDG_PC_WIDE_WIDTHS.map((width) =>
+ GDG_INPUT_STATES.map((state) => (
+
+ ))
+ )}
+
+
+
+
mobile widths
+
+ {GDG_MOBILE_WIDTHS.map((width) =>
+ GDG_INPUT_STATES.map((state) => (
+
+ ))
+ )}
+
+
+
+
+
+
+
+ {GDG_TEXTAREA_DEVICES.map((device) =>
+ GDG_TEXTAREA_STATES.map((state) => (
+
+ ))
+ )}
+
+
+
+
+
+
+
pc
+ {GDG_SEARCH_FIELD_WIDTHS.filter((w) => w !== 'twoThirds').map((width) => (
+
+ ))}
+
+
+
mobile
+ {GDG_SEARCH_FIELD_WIDTHS.filter((w) => w !== 'half').map((width) => (
+
+
{`mobile-${width}`}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
pc / wide
+
+ {DROPDOWN_PC_WIDE_SIZES.map((size) => (
+
+ ))}
+
+
+
+
mobile
+
+ {DROPDOWN_MOBILE_SIZES.map((size) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {GDG_CHECKBOX_SIZES.map((size) => (
+
+
{size}
+
+
+ setCheckboxChecked((prev) => ({ ...prev, [size]: checked }))
+ }
+ />
+
+ {checkboxChecked[size] ? 'checked' : 'unchecked'}
+
+
+
+
+
+ setRadioChecked((prev) => ({ ...prev, [size]: 'left' }))}
+ />
+ setRadioChecked((prev) => ({ ...prev, [size]: 'right' }))}
+ />
+
+
+
+
+ ))}
+
+
+
+
+
+ {GDG_FILE_CARD_DEVICES.map((device) => (
+
+
{device}
+
+ {GDG_FILE_CARD_ACTIONS.map((action) => (
+
+ ))}
+ {GDG_FILE_CARD_ACTIONS.map((action) => (
+
+ ))}
+
+
+
+
+ ))}
+
+
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgButton.tsx b/src/components/ui/design-system/GdgButton.tsx
new file mode 100644
index 0000000..10c8ec8
--- /dev/null
+++ b/src/components/ui/design-system/GdgButton.tsx
@@ -0,0 +1,142 @@
+'use client'
+
+import Link from 'next/link'
+import type { AnchorHTMLAttributes, ButtonHTMLAttributes, ReactNode } from 'react'
+import { cn } from '@/utils/cn'
+import { getMobileWidthClass, getPcWidthClass, isWideOnlyWidth } from './controlMeta'
+import type { Device, PcWidthVariant, WidthToken } from './controlMeta'
+
+export const GDG_BUTTON_SIZES = ['large', 'small'] as const
+export type ButtonSize = (typeof GDG_BUTTON_SIZES)[number]
+
+export const GDG_BUTTON_VARIANTS = [
+ 'default',
+ 'active',
+ 'pressed',
+ 'disabled',
+ 'white',
+ 'bordered'
+] as const
+export type Variant = (typeof GDG_BUTTON_VARIANTS)[number]
+
+type ButtonLike = ButtonHTMLAttributes & AnchorHTMLAttributes
+
+export type GdgButtonProps = {
+ as?: 'button' | 'a'
+ device?: Device
+ size?: ButtonSize
+ variant?: Variant
+ widthToken?: WidthToken
+ pcVariant?: PcWidthVariant
+ fullWidth?: boolean
+ icon?: ReactNode
+ loading?: boolean
+} & Omit
+
+const SIZE_CLASS: Record> = {
+ pc: {
+ large: 'h-13 px-12 text-base leading-6',
+ small: 'h-11 px-6 text-base leading-6 w-30.5'
+ },
+ mobile: {
+ large: 'h-12 px-8 text-base leading-6',
+ small: 'h-11 px-5 text-sm leading-5 w-27.25'
+ }
+}
+
+const VARIANT_CLASS: Record = {
+ default: 'bg-gray-100 text-white border-gray-100',
+ active: 'bg-red text-white border-red shadow-[0px_2px_50px_rgba(0,0,0,0.5)]',
+ pressed: 'bg-red-400 text-white border-red',
+ disabled: 'bg-gray-400 text-white/70 border-gray-400',
+ white: 'bg-white text-black border-white shadow-[0px_5px_35px_rgba(18,18,18,0.05)]',
+ bordered: 'bg-black text-white border-gray-800 hover:border-gray-900 transition-colors'
+}
+
+export function GdgButton({
+ as = 'button',
+ device = 'pc',
+ size = 'large',
+ variant = 'default',
+ widthToken,
+ pcVariant,
+ fullWidth,
+ icon,
+ loading,
+ className,
+ children,
+ href,
+ disabled,
+ ...rest
+}: GdgButtonProps) {
+ const isDisabled = disabled || variant === 'disabled' || loading
+ const effectiveVariant = isDisabled ? 'disabled' : variant
+
+ const defaultPcVariant: PcWidthVariant =
+ widthToken && isWideOnlyWidth(widthToken) ? 'wide' : 'narrow'
+ const resolvedPcVariant = pcVariant ?? defaultPcVariant
+
+ const widthClass = fullWidth
+ ? 'w-full'
+ : widthToken
+ ? device === 'pc'
+ ? getPcWidthClass(widthToken, resolvedPcVariant)
+ : getMobileWidthClass(widthToken)
+ : SIZE_CLASS[device][size].split(' ').find((c) => c.startsWith('w-')) || ''
+
+ const classes = cn(
+ 'inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-full border font-medium transition-all duration-200 ease-out focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/40 font-pretendard shrink-0',
+ SIZE_CLASS[device][size].replace(/w-\S+/g, ''),
+ widthClass,
+ VARIANT_CLASS[effectiveVariant],
+ effectiveVariant === 'white' && (device === 'pc' ? 'typo-pc-s3' : 'typo-m-s2'),
+ isDisabled && 'pointer-events-none cursor-not-allowed opacity-70',
+ className
+ )
+
+ const inner = (
+ <>
+ {loading && (
+
+ )}
+ {icon && (
+
+ {icon}
+
+ )}
+ {children}
+ >
+ )
+
+ const commonProps = {
+ className: classes,
+ style: rest.style,
+ 'aria-disabled': isDisabled
+ }
+
+ if (as === 'a') {
+ return (
+ )}
+ href={href ?? '#'}
+ {...commonProps}
+ >
+ {inner}
+
+ )
+ }
+
+ return (
+ )}
+ type={(rest as ButtonHTMLAttributes).type ?? 'button'}
+ disabled={isDisabled}
+ {...commonProps}
+ >
+ {inner}
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgCheckbox.tsx b/src/components/ui/design-system/GdgCheckbox.tsx
new file mode 100644
index 0000000..265bd3c
--- /dev/null
+++ b/src/components/ui/design-system/GdgCheckbox.tsx
@@ -0,0 +1,74 @@
+'use client'
+
+import type { ButtonHTMLAttributes } from 'react'
+import { cn } from '@/utils/cn'
+
+export const GDG_CHECKBOX_SIZES = ['pc', 'mobile'] as const
+
+export type GdgCheckboxProps = {
+ checked?: boolean
+ onCheckedChange?: (checked: boolean) => void
+ size?: (typeof GDG_CHECKBOX_SIZES)[number]
+} & Omit, 'onChange'>
+
+const SIZE_CLASS: Record<'pc' | 'mobile', string> = {
+ pc: 'size-[20px]',
+ mobile: 'size-[18px]'
+}
+
+export function GdgCheckbox({
+ checked = false,
+ onCheckedChange,
+ size = 'pc',
+ className,
+ disabled,
+ ...rest
+}: GdgCheckboxProps) {
+ return (
+ {
+ if (disabled) return
+ rest.onClick?.(event)
+ onCheckedChange?.(!checked)
+ }}
+ onKeyDown={(event) => {
+ if (disabled) return
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault()
+ onCheckedChange?.(!checked)
+ }
+ rest.onKeyDown?.(event)
+ }}
+ {...rest}
+ className={cn(
+ 'inline-flex items-center justify-center rounded-md border transition-colors duration-150 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/40',
+ SIZE_CLASS[size],
+ checked ? 'border-red bg-red' : 'border-white',
+ disabled && 'opacity-60 cursor-not-allowed',
+ className
+ )}
+ >
+ {checked && (
+
+
+
+ )}
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgColorTag.tsx b/src/components/ui/design-system/GdgColorTag.tsx
new file mode 100644
index 0000000..80cb914
--- /dev/null
+++ b/src/components/ui/design-system/GdgColorTag.tsx
@@ -0,0 +1,84 @@
+'use client'
+
+import type { ComponentPropsWithoutRef } from 'react'
+import { cn } from '@/utils/cn'
+
+export const GDG_COLOR_TAG_COLORS = ['red', 'blue', 'green', 'yellow', 'white'] as const
+export type GdgTagColor = (typeof GDG_COLOR_TAG_COLORS)[number]
+
+export const GDG_COLOR_TAG_FILLS = ['off', 'on', 'half'] as const
+export type GdgTagFill = (typeof GDG_COLOR_TAG_FILLS)[number]
+
+export const GDG_COLOR_TAG_SIZES = ['pc', 'mobile', 'mini'] as const
+export type GdgTagSize = (typeof GDG_COLOR_TAG_SIZES)[number]
+
+const COLOR_META: Record = {
+ red: {
+ fill: 'bg-red border-red text-white shadow-[0px_2px_40px_rgba(0,0,0,0.35)]',
+ outline: 'border-red text-red',
+ half: 'bg-red-400 border-red-400 text-red'
+ },
+ blue: {
+ fill: 'bg-blue border-blue text-white shadow-[0px_2px_40px_rgba(0,0,0,0.35)]',
+ outline: 'border-blue text-blue',
+ half: 'bg-blue-400 border-blue-400 text-blue'
+ },
+ green: {
+ fill: 'bg-green border-green text-white shadow-[0px_2px_40px_rgba(0,0,0,0.35)]',
+ outline: 'border-green text-green',
+ half: 'bg-green-400 border-green-400 text-green'
+ },
+ yellow: {
+ fill: 'bg-yellow border-yellow text-black shadow-[0px_2px_40px_rgba(0,0,0,0.35)]',
+ outline: 'border-yellow text-yellow',
+ half: 'bg-yellow-400 border-yellow-400 text-yellow'
+ },
+ white: {
+ fill: 'bg-white border-white text-black shadow-[0px_2px_40px_rgba(0,0,0,0.35)]',
+ outline: 'border-white text-white',
+ half: 'bg-gray-200 border-gray-200 text-gray-700'
+ }
+}
+
+const SIZE_CLASS: Record = {
+ pc: 'h-[25px] px-4 text-[16px] leading-[20px] gap-2',
+ mobile: 'h-[25px] px-3 text-[14px] leading-[18px] gap-1.5',
+ mini: 'h-[19px] px-2.5 text-[12px] leading-[16px] gap-1'
+}
+
+export interface GdgColorTagProps extends ComponentPropsWithoutRef<'span'> {
+ size?: GdgTagSize
+ color?: GdgTagColor
+ fill?: GdgTagFill
+}
+
+export function GdgColorTag({
+ size = 'pc',
+ color = 'red',
+ fill = 'on',
+ className,
+ style,
+ children,
+ ...rest
+}: GdgColorTagProps) {
+ const meta = COLOR_META[color]
+ const baseClass = SIZE_CLASS[size]
+
+ const fillClass = fill === 'on' ? meta.fill : fill === 'off' ? meta.outline : ''
+
+ return (
+
+ {children}
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgDropdown.module.css b/src/components/ui/design-system/GdgDropdown.module.css
new file mode 100644
index 0000000..88ae742
--- /dev/null
+++ b/src/components/ui/design-system/GdgDropdown.module.css
@@ -0,0 +1,26 @@
+.root {}
+
+.root :global(.gdg-dropdown-trigger [data-slot='input-wrapper']) {
+ border: 0;
+ outline: 0;
+ box-shadow: none;
+ background: transparent;
+}
+
+.root :global(.gdg-dropdown-trigger [data-slot='selector-button']) {
+ border: 0;
+ outline: 0;
+ box-shadow: none;
+}
+
+.root :global(.gdg-dropdown-trigger[data-focus-visible='true']),
+.root :global(.gdg-dropdown-trigger [data-focus-visible='true']) {
+ outline: 0;
+ box-shadow: none;
+}
+
+.root :global(.gdg-dropdown-popover [data-top-scroll]),
+.root :global(.gdg-dropdown-popover [data-bottom-scroll]) {
+ mask-image: none !important;
+ -webkit-mask-image: none !important;
+}
diff --git a/src/components/ui/design-system/GdgDropdown.tsx b/src/components/ui/design-system/GdgDropdown.tsx
new file mode 100644
index 0000000..20eff71
--- /dev/null
+++ b/src/components/ui/design-system/GdgDropdown.tsx
@@ -0,0 +1,244 @@
+'use client'
+
+import { Autocomplete, AutocompleteItem, AutocompleteSection } from '@nextui-org/react'
+import { type Key, useCallback, useEffect, useMemo, useState } from 'react'
+import { cn } from '@/utils/cn'
+import styles from './GdgDropdown.module.css'
+import {
+ CONTROL_META,
+ getControlMeta,
+ getMobileWidthClass,
+ getPlaceholderTypoByText,
+ getPcWidthClass,
+ isWideOnlyWidth
+} from './controlMeta'
+import type { Device, PcWidthVariant, WidthToken } from './controlMeta'
+
+export type GdgDropdownOption = {
+ id: string
+ label: string
+}
+
+export type GdgDropdownOptionGroup = {
+ title: string
+ items: GdgDropdownOption[]
+}
+
+export type GdgDropdownSize = Exclude
+
+export interface GdgDropdownProps {
+ device?: Device
+ size?: GdgDropdownSize
+ pcVariant?: PcWidthVariant
+ label?: string
+ placeholder?: string
+ helperText?: string
+ options?: GdgDropdownOption[]
+ optionGroups?: GdgDropdownOptionGroup[]
+ value?: string
+ defaultValue?: string
+ onChange?: (value: string) => void
+ disabled?: boolean
+ autoFocus?: boolean
+ isInvalid?: boolean
+ errorMessage?: string
+}
+
+const POPOVER_MAX_HEIGHT_CLASS: Record = {
+ pc: 'max-h-75',
+ mobile: 'max-h-66.5'
+}
+
+export function GdgDropdown({
+ device = 'pc',
+ size = 'small',
+ pcVariant,
+ label,
+ placeholder = '선택하세요',
+ helperText,
+ options = [],
+ optionGroups,
+ value,
+ defaultValue,
+ onChange,
+ disabled,
+ autoFocus,
+ isInvalid,
+ errorMessage
+}: GdgDropdownProps) {
+ const controlMeta = getControlMeta(device, size)
+ const ITEM_CLASS = cn(
+ 'flex h-9 items-center justify-between rounded-lg px-2 text-white outline-none transition-colors',
+ controlMeta.text,
+ 'hover:bg-white hover:text-black',
+ 'selected:font-medium',
+ 'focus:bg-white focus:text-black',
+ 'hover:[&_[data-slot=selected-icon]]:text-black',
+ 'focus:[&_[data-slot=selected-icon]]:text-black'
+ )
+ const [internalValue, setInternalValue] = useState(defaultValue ?? '')
+ const currentValue = value ?? internalValue
+ const isGrouped = Boolean(optionGroups?.length)
+ const hasError = Boolean(isInvalid)
+
+ const resolvedOptions = useMemo(
+ () => (optionGroups ? optionGroups.flatMap((group) => group.items) : options),
+ [optionGroups, options]
+ )
+
+ useEffect(() => {
+ if (value !== undefined) {
+ setInternalValue(value)
+ }
+ }, [value])
+
+ const filterFn = useCallback((textValue: string, inputValue: string) => {
+ const needle = inputValue.trim().toLowerCase()
+ if (!needle) return true
+ return textValue.toLowerCase().includes(needle)
+ }, [])
+
+ const defaultPcVariant: PcWidthVariant = isWideOnlyWidth(size) ? 'wide' : 'narrow'
+ const resolvedPcVariant = pcVariant ?? defaultPcVariant
+ const widthClass =
+ device === 'pc' ? getPcWidthClass(size, resolvedPcVariant) : getMobileWidthClass(size)
+ const isMini = device === 'pc' && size === 'mini'
+
+ // Fixed border color to gray-800 unless it's an error.
+ const baseClass = cn(
+ 'rounded-full bg-black border transition-colors',
+ controlMeta.height,
+ hasError ? 'border-red' : 'border-gray-800',
+ hasError ? 'hover:border-red' : '',
+ hasError ? 'focus:border-red' : '',
+ 'disabled:bg-gray-100 disabled:border-gray-100 disabled:cursor-not-allowed disabled:text-white/40'
+ )
+
+ const selectorClass = cn(
+ 'h-full w-auto min-w-0 mx-0 justify-between bg-transparent font-medium text-white hover:bg-transparent focus-visible:outline-none',
+ isMini && 'px-0 py-0 [&_[data-slot=inner-wrapper]]:h-auto',
+ controlMeta.text,
+ 'disabled:text-white/40 disabled:cursor-not-allowed'
+ )
+
+ const caption = hasError ? (errorMessage ?? helperText) : helperText
+
+ const handleSelectionChange = useCallback(
+ (next: Key | null) => {
+ if (next == null) return
+
+ const nextValue = next ? String(next) : ''
+ if (value === undefined) {
+ setInternalValue(nextValue)
+ }
+ onChange?.(nextValue)
+ },
+ [onChange, value]
+ )
+
+ return (
+
+ {label && (
+
{label}
+ )}
+
+ {isGrouped
+ ? optionGroups?.map((group) => (
+
+ {group.items.map((item) => (
+
+ {item.label}
+
+ ))}
+
+ ))
+ : resolvedOptions.map((option) => (
+
+ {option.label}
+
+ ))}
+
+ {caption && (
+
{caption}
+ )}
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgFieldContainer.tsx b/src/components/ui/design-system/GdgFieldContainer.tsx
new file mode 100644
index 0000000..eba1544
--- /dev/null
+++ b/src/components/ui/design-system/GdgFieldContainer.tsx
@@ -0,0 +1,52 @@
+'use client'
+
+import { type ReactNode } from 'react'
+import { cn } from '@/utils/cn'
+
+export type GdgFieldStatus = 'success' | 'error'
+
+export interface GdgFieldContainerProps {
+ label: string
+ required?: boolean
+ caption?: string
+ status?: GdgFieldStatus
+ statusMessage?: string
+ children: ReactNode
+ action?: ReactNode
+}
+
+export function GdgFieldContainer({
+ label,
+ required = false,
+ caption,
+ status,
+ statusMessage,
+ children,
+ action
+}: GdgFieldContainerProps) {
+ const hasStatus = Boolean(status && statusMessage)
+
+ return (
+
+
+
{label}
+ {required &&
*
}
+
+
+
+ {children}
+ {action}
+
+
+ {caption && !hasStatus && (
+
{caption}
+ )}
+
+ {hasStatus && (
+
+ {statusMessage}
+
+ )}
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgFileCard.tsx b/src/components/ui/design-system/GdgFileCard.tsx
new file mode 100644
index 0000000..46a7e31
--- /dev/null
+++ b/src/components/ui/design-system/GdgFileCard.tsx
@@ -0,0 +1,163 @@
+'use client'
+
+import Image from 'next/image'
+import { type ComponentPropsWithoutRef, type MouseEventHandler } from 'react'
+import { cn } from '@/utils/cn'
+
+export const GDG_FILE_CARD_ACTIONS = ['remove', 'download', 'none'] as const
+export type GdgFileCardAction = (typeof GDG_FILE_CARD_ACTIONS)[number]
+
+export const GDG_FILE_CARD_DEVICES = ['pc', 'mobile', 'auto'] as const
+export type GdgFileCardDevice = (typeof GDG_FILE_CARD_DEVICES)[number]
+
+export interface GdgFileCardProps extends ComponentPropsWithoutRef<'div'> {
+ device?: GdgFileCardDevice
+ fileName: string
+ fileSize?: string
+ action?: GdgFileCardAction
+ onAction?: MouseEventHandler
+ showFileIcon?: boolean
+ fullWidth?: boolean
+}
+
+const FileIcon = ({ size }: { size: 'pc' | 'mobile' }) => (
+
+)
+
+const ActionIcon = ({ src, alt }: { src: string; alt: string }) => (
+
+)
+
+/**
+ * GdgFileCard Component
+ * Aligned with Figma design node 544:1653.
+ * The file info is contained within a gray card, and the action icon is placed outside to the right.
+ */
+export function GdgFileCard({
+ device = 'auto',
+ action = 'remove',
+ fileName,
+ fileSize,
+ onAction,
+ className,
+ showFileIcon = true,
+ fullWidth = false,
+ ...rest
+}: GdgFileCardProps) {
+ const actionIcon =
+ action === 'remove' ? (
+
+ ) : action === 'download' ? (
+
+ ) : null
+
+ const widthClass = fullWidth
+ ? 'w-full'
+ : device === 'auto'
+ ? 'pc:w-137.5 mobile:w-85.75'
+ : device === 'pc'
+ ? 'w-137.5'
+ : 'w-85.75'
+
+ const heightClass = device === 'auto' ? 'pc:h-11 mobile:h-10' : device === 'pc' ? 'h-11' : 'h-10'
+
+ const contentClass =
+ device === 'auto'
+ ? 'pc:gap-4 pc:typo-pc-b2 mobile:gap-3 mobile:typo-m-b3'
+ : device === 'pc'
+ ? 'gap-4 typo-pc-b2'
+ : 'gap-3 typo-m-b3'
+
+ const fileSizeClass =
+ device === 'auto'
+ ? 'pc:typo-pc-b3 mobile:typo-m-c1'
+ : device === 'pc'
+ ? 'typo-pc-b3'
+ : 'typo-m-c1'
+
+ const resolvedDevice = device === 'auto' ? 'pc' : device
+
+ return (
+
+
+
+ {showFileIcon &&
}
+
{fileName}
+
+ {fileSize && (
+
{fileSize}
+ )}
+
+ {actionIcon && (
+
+ {actionIcon}
+
+ )}
+
+ )
+}
+
+export interface GdgUploadButtonProps extends ComponentPropsWithoutRef<'button'> {
+ device?: 'pc' | 'mobile' | 'auto'
+ label?: string
+}
+
+/**
+ * GdgUploadButton Component
+ * Red CTA button for file selection.
+ * Aligned with Figma design node 544:1653.
+ */
+export function GdgUploadButton({
+ device = 'auto',
+ label = '+ 파일 선택',
+ className,
+ ...rest
+}: GdgUploadButtonProps) {
+ const widthClass =
+ device === 'auto' ? 'pc:w-137.5 mobile:w-85.75' : device === 'pc' ? 'w-137.5' : 'w-85.75'
+
+ const heightClass = device === 'auto' ? 'pc:h-11 mobile:h-10' : device === 'pc' ? 'h-11' : 'h-10'
+
+ const typoClass =
+ device === 'auto'
+ ? 'pc:typo-pc-b2 mobile:typo-m-b3'
+ : device === 'pc'
+ ? 'typo-pc-b2'
+ : 'typo-m-b3'
+
+ return (
+
+ {label}
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgGoogleLoginButton.tsx b/src/components/ui/design-system/GdgGoogleLoginButton.tsx
new file mode 100644
index 0000000..b694324
--- /dev/null
+++ b/src/components/ui/design-system/GdgGoogleLoginButton.tsx
@@ -0,0 +1,42 @@
+'use client'
+
+import Image from 'next/image'
+import { GdgButton } from './GdgButton'
+import type { Device } from './controlMeta'
+
+export interface GdgGoogleLoginButtonProps {
+ device?: Device
+ onClick?: () => void
+ disabled?: boolean
+ loading?: boolean
+}
+
+/**
+ * GdgGoogleLoginButton component
+ * Strictly follows Figma design: PC (300x52), Mobile (260x46)
+ * Background is always white, text is black.
+ */
+export function GdgGoogleLoginButton({
+ device = 'pc',
+ onClick,
+ disabled,
+ loading
+}: GdgGoogleLoginButtonProps) {
+ // Fixed dimensions from design spec
+ const sizeClasses = device === 'pc' ? 'w-[300px] h-[52px]' : 'w-[260px] h-[46px]'
+ const typoClasses = 'typo-pc-s2 mobile:typo-m-s3'
+
+ return (
+ }
+ >
+ Google 계정으로 로그인
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgInputField.tsx b/src/components/ui/design-system/GdgInputField.tsx
new file mode 100644
index 0000000..f357b17
--- /dev/null
+++ b/src/components/ui/design-system/GdgInputField.tsx
@@ -0,0 +1,150 @@
+'use client'
+
+import { forwardRef, type InputHTMLAttributes, type ReactNode, useId } from 'react'
+import { cn } from '@/utils/cn'
+import type { Device, PcWidthVariant, SizeToken, WidthToken } from './controlMeta'
+import {
+ CONTROL_META,
+ getMobileWidthClass,
+ getPcWidthClass,
+ getPlaceholderTypoByText,
+ isWideOnlyWidth
+} from './controlMeta'
+
+export const GDG_INPUT_STATES = ['available', 'disabled', 'error'] as const
+export type InputState = (typeof GDG_INPUT_STATES)[number]
+
+const STATE_CLASS: Record<
+ InputState,
+ {
+ wrapper: string
+ input: string
+ adornment: string
+ helper: string
+ }
+> = {
+ available: {
+ wrapper: 'border-gray-800 bg-black text-white',
+ input: 'text-white placeholder:text-gray-700',
+ adornment: 'text-white',
+ helper: 'typo-pc-c1 mobile:typo-m-c1 pl-2 text-gray-600'
+ },
+ error: {
+ wrapper: 'border-red bg-black text-white',
+ input: 'text-white placeholder:text-gray-700',
+ adornment: 'text-red',
+ helper: 'typo-pc-c1 mobile:typo-m-c1 pl-2 text-red'
+ },
+ disabled: {
+ wrapper: 'border-gray-400 bg-gray-400 text-gray-900 cursor-not-allowed',
+ input: 'text-gray-900 placeholder:text-gray-700 cursor-not-allowed',
+ adornment: 'text-gray-900',
+ helper: 'typo-pc-c1 mobile:typo-m-c1 pl-2 text-gray-900'
+ }
+}
+
+export interface GdgInputFieldProps extends Omit, 'size'> {
+ device?: Device
+ width?: WidthToken
+ density?: SizeToken
+ pcVariant?: PcWidthVariant
+ state?: InputState
+ label?: string
+ helperText?: string
+ errorText?: string
+ startAdornment?: ReactNode
+ endAdornment?: ReactNode
+ fullWidth?: boolean
+}
+
+export const GdgInputField = forwardRef(
+ (
+ {
+ device = 'pc',
+ width = 'full',
+ density,
+ pcVariant,
+ state,
+ label,
+ helperText,
+ errorText,
+ startAdornment,
+ endAdornment,
+ className,
+ style,
+ fullWidth,
+ disabled,
+ id,
+ ...rest
+ },
+ ref
+ ) => {
+ const generatedId = useId()
+ const fieldId = id ?? generatedId
+ const computedState: InputState = disabled ? 'disabled' : (state ?? 'available')
+ const effectiveDensity: SizeToken = density ?? (width === 'mini' ? 'mini' : 'default')
+ const defaultPcVariant: PcWidthVariant = isWideOnlyWidth(width) ? 'wide' : 'narrow'
+ const resolvedPcVariant = pcVariant ?? defaultPcVariant
+
+ const widthClass = fullWidth
+ ? 'w-full'
+ : device === 'pc'
+ ? getPcWidthClass(width, resolvedPcVariant)
+ : getMobileWidthClass(width)
+
+ const controlMeta =
+ effectiveDensity === 'mini'
+ ? (CONTROL_META[device].mini ?? CONTROL_META[device].default)
+ : CONTROL_META[device].default
+
+ return (
+
+ {label && {label} }
+
+ {startAdornment && (
+
+ {startAdornment}
+
+ )}
+
+ {endAdornment && (
+
+ {endAdornment}
+
+ )}
+
+ {(helperText || errorText) && (
+
+ {computedState === 'error' ? (errorText ?? helperText) : helperText}
+
+ )}
+
+ )
+ }
+)
+
+GdgInputField.displayName = 'GdgInputField'
diff --git a/src/components/ui/design-system/GdgLogo.tsx b/src/components/ui/design-system/GdgLogo.tsx
new file mode 100644
index 0000000..d0def70
--- /dev/null
+++ b/src/components/ui/design-system/GdgLogo.tsx
@@ -0,0 +1,137 @@
+'use client'
+
+import Image from 'next/image'
+
+import gdgocPcLogo from '@public/icons/gdgocIcon/pc.svg'
+import gdgocMobileLogo from '@public/icons/gdgocIcon/mobile.svg'
+
+import { cn } from '@/utils/cn'
+
+export const GDG_LOGO_MODES = ['auto', 'pc', 'mobile'] as const
+export type LogoMode = (typeof GDG_LOGO_MODES)[number]
+
+export const GDG_LOGO_VARIANTS = ['icon', 'short', 'long'] as const
+export type LogoVariant = (typeof GDG_LOGO_VARIANTS)[number]
+
+const LOGO_ASSETS = {
+ pc: {
+ src: gdgocPcLogo,
+ width: gdgocPcLogo.width,
+ height: gdgocPcLogo.height,
+ alt: 'GDGoC INHA desktop logo'
+ },
+ mobile: {
+ src: gdgocMobileLogo,
+ width: gdgocMobileLogo.width,
+ height: gdgocMobileLogo.height,
+ alt: 'GDGoC INHA mobile logo'
+ }
+} as const
+
+export interface GdgLogoProps {
+ mode?: LogoMode
+ variant?: LogoVariant
+ className?: string
+ priority?: boolean
+}
+
+export function GdgLogo({
+ mode = 'auto',
+ variant = 'icon',
+ className,
+ priority = false
+}: GdgLogoProps) {
+ const renderLogo = (target: 'pc' | 'mobile') => {
+ const asset = LOGO_ASSETS[target]
+ const isPc = target === 'pc'
+
+ if (variant === 'icon') {
+ return (
+
+
+
+ )
+ }
+
+ return (
+
+
+ {variant === 'long' ? (
+
+
+ Google Developer Group
+
+
+ Inha University
+
+
+ ) : (
+ <>
+
+ Google
+
+ Developer
+
+ Group
+
+
+ Inha University
+
+ >
+ )}
+
+ )
+ }
+
+ if (mode === 'pc') {
+ return renderLogo('pc')
+ }
+
+ if (mode === 'mobile') {
+ return renderLogo('mobile')
+ }
+
+ return (
+ <>
+ {renderLogo('pc')}
+ {renderLogo('mobile')}
+ >
+ )
+}
diff --git a/src/components/ui/design-system/GdgMajorDropdown.tsx b/src/components/ui/design-system/GdgMajorDropdown.tsx
new file mode 100644
index 0000000..ef73a9f
--- /dev/null
+++ b/src/components/ui/design-system/GdgMajorDropdown.tsx
@@ -0,0 +1,44 @@
+'use client'
+
+import { majorOptions } from '@/constant/majorOptions'
+import { GdgDropdown, type GdgDropdownOptionGroup } from './GdgDropdown'
+
+export type GdgMajorDropdownProps = {
+ value: string
+ onChangeAction: (value: string) => void
+ autoFocus?: boolean
+ isInvalid?: boolean
+ errorMessage?: string
+ device?: 'pc' | 'mobile' | 'auto'
+}
+
+export function GdgMajorDropdown({
+ value,
+ onChangeAction,
+ autoFocus,
+ isInvalid,
+ errorMessage,
+ device = 'auto'
+}: GdgMajorDropdownProps) {
+ const groupedOptions: GdgDropdownOptionGroup[] = majorOptions.map((group) => ({
+ title: group.title,
+ items: group.items.map((item) => ({
+ id: item.value,
+ label: item.value
+ }))
+ }))
+
+ return (
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgRadio.tsx b/src/components/ui/design-system/GdgRadio.tsx
new file mode 100644
index 0000000..ac905a6
--- /dev/null
+++ b/src/components/ui/design-system/GdgRadio.tsx
@@ -0,0 +1,58 @@
+'use client'
+
+import type { ButtonHTMLAttributes } from 'react'
+import { cn } from '@/utils/cn'
+
+export const GDG_RADIO_SIZES = ['pc', 'mobile'] as const
+
+export type GdgRadioProps = {
+ checked?: boolean
+ onCheckedChange?: (checked: boolean) => void
+ size?: (typeof GDG_RADIO_SIZES)[number]
+} & Omit, 'onChange'>
+
+const SIZE_CLASS: Record<'pc' | 'mobile', string> = {
+ pc: 'size-[20px]',
+ mobile: 'size-[18px]'
+}
+
+export function GdgRadio({
+ checked = false,
+ onCheckedChange,
+ size = 'pc',
+ className,
+ disabled,
+ ...rest
+}: GdgRadioProps) {
+ return (
+ {
+ if (disabled || checked) return
+ rest.onClick?.(event)
+ onCheckedChange?.(true)
+ }}
+ onKeyDown={(event) => {
+ if (disabled) return
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault()
+ if (!checked) onCheckedChange?.(true)
+ }
+ rest.onKeyDown?.(event)
+ }}
+ {...rest}
+ className={cn(
+ 'inline-flex items-center justify-center rounded-full border transition-colors duration-150 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-white/40',
+ SIZE_CLASS[size],
+ checked ? 'border-red' : 'border-white',
+ disabled && 'opacity-60 cursor-not-allowed',
+ className
+ )}
+ >
+ {checked && }
+
+ )
+}
diff --git a/src/components/ui/design-system/GdgSearchField.tsx b/src/components/ui/design-system/GdgSearchField.tsx
new file mode 100644
index 0000000..f33b499
--- /dev/null
+++ b/src/components/ui/design-system/GdgSearchField.tsx
@@ -0,0 +1,79 @@
+'use client'
+
+import { forwardRef, useId, type InputHTMLAttributes } from 'react'
+import { cn } from '@/utils/cn'
+import { Search } from 'lucide-react'
+import {
+ CONTROL_META,
+ getMobileWidthClass,
+ getPcWidthClass,
+ getPlaceholderTypoByText
+} from './controlMeta'
+
+export const GDG_SEARCH_FIELD_DEVICES = ['pc', 'mobile'] as const
+export const GDG_SEARCH_FIELD_WIDTHS = ['full', 'half', 'twoThirds'] as const
+
+export interface GdgSearchFieldProps extends Omit, 'size'> {
+ device?: (typeof GDG_SEARCH_FIELD_DEVICES)[number]
+ width?: (typeof GDG_SEARCH_FIELD_WIDTHS)[number]
+ label?: string
+}
+
+export const GdgSearchField = forwardRef(
+ ({ device = 'pc', width = 'full', label, className, id, style, ...rest }, ref) => {
+ const generatedId = useId()
+ const fieldId = id ?? generatedId
+
+ const isPc = device === 'pc'
+
+ // Determine specific width class based on device and design spec
+ const widthClass = isPc
+ ? width === 'full' ? 'w-280' : width === 'half' ? 'w-103' : 'w-fit'
+ : width === 'full' ? 'w-85.75' : width === 'twoThirds' ? 'w-56.5' : 'w-fit'
+
+ const controlMeta = CONTROL_META[device].default
+
+ return (
+
+ {label && (
+
+ {label}
+
+ )}
+
+ {(isPc && width === 'full') && }
+
+
+
+ {!(isPc && width === 'full') && }
+
+
+ )
+ }
+)
+
+GdgSearchField.displayName = 'GdgSearchField'
diff --git a/src/components/ui/design-system/GdgSegmentedButton.tsx b/src/components/ui/design-system/GdgSegmentedButton.tsx
new file mode 100644
index 0000000..5ac58d2
--- /dev/null
+++ b/src/components/ui/design-system/GdgSegmentedButton.tsx
@@ -0,0 +1,60 @@
+'use client'
+
+import type { ButtonHTMLAttributes } from 'react'
+import { cn } from '@/utils/cn'
+
+export const GDG_SEGMENTED_BUTTON_DEVICES = ['pc', 'mobile'] as const
+export const GDG_SEGMENTED_BUTTON_EDGES = ['left', 'right'] as const
+
+export type GdgSegmentedButtonProps = {
+ device?: (typeof GDG_SEGMENTED_BUTTON_DEVICES)[number]
+ edge?: (typeof GDG_SEGMENTED_BUTTON_EDGES)[number]
+ pressed?: boolean
+} & ButtonHTMLAttributes
+
+const EDGE_RADIUS: Record<'pc' | 'mobile', Record<'left' | 'right', string>> = {
+ pc: {
+ left: 'rounded-bl-[999px] rounded-tl-[999px]',
+ right: 'rounded-br-[999px] rounded-tr-[999px]'
+ },
+ mobile: {
+ left: 'rounded-bl-[999px] rounded-tl-[999px]',
+ right: 'rounded-br-[999px] rounded-tr-[999px]'
+ }
+}
+
+const STATE_CLASS = {
+ pressed: 'bg-red border-red text-white',
+ default: 'bg-gray-100 border-gray-100 text-white',
+ disabled: 'bg-gray-400 border-gray-400 text-white/70'
+}
+
+export function GdgSegmentedButton({
+ device = 'pc',
+ edge = 'left',
+ pressed = false,
+ className,
+ style,
+ children,
+ ...rest
+}: GdgSegmentedButtonProps) {
+ const isDisabled = Boolean(rest.disabled)
+
+ return (
+
+ {children}
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/ui/design-system/GdgTag.tsx b/src/components/ui/design-system/GdgTag.tsx
new file mode 100644
index 0000000..8e432b0
--- /dev/null
+++ b/src/components/ui/design-system/GdgTag.tsx
@@ -0,0 +1,57 @@
+'use client'
+
+import type { HTMLAttributes, ReactNode } from 'react'
+import { cn } from '@/utils/cn'
+
+export const GDG_TAG_VARIANTS = ['default', 'active', 'interactive', 'disabled'] as const
+export type GdgTagVariant = (typeof GDG_TAG_VARIANTS)[number]
+
+export const GDG_TAG_DEVICES = ['pc', 'mobile'] as const
+export type GdgTagDevice = (typeof GDG_TAG_DEVICES)[number]
+
+export type GdgTagProps = {
+ device?: GdgTagDevice
+ variant?: GdgTagVariant
+ leadingIcon?: ReactNode
+ trailingIcon?: ReactNode
+} & HTMLAttributes
+
+const DEVICE_SIZE: Record = {
+ pc: 'h-[32px] px-4 text-[16px] leading-[24px] gap-2',
+ mobile: 'h-[30px] px-3 text-[14px] leading-[20px] gap-1.5'
+}
+
+const VARIANT_CLASS: Record = {
+ default: 'border-gray-400 text-white/80',
+ active: 'bg-red border-red text-white shadow-[0px_2px_50px_rgba(0,0,0,0.35)]',
+ interactive: 'border-white/40 text-white hover:border-red hover:text-red',
+ disabled: 'border-gray-400 text-white/40 cursor-not-allowed'
+}
+
+export function GdgTag({
+ device = 'pc',
+ variant = 'default',
+ leadingIcon,
+ trailingIcon,
+ className,
+ style,
+ children,
+ ...rest
+}: GdgTagProps) {
+ return (
+
+ {leadingIcon && {leadingIcon} }
+ {children}
+ {trailingIcon && {trailingIcon} }
+
+ )
+}
\ No newline at end of file
diff --git a/src/components/ui/design-system/GdgTextarea.tsx b/src/components/ui/design-system/GdgTextarea.tsx
new file mode 100644
index 0000000..2fc6954
--- /dev/null
+++ b/src/components/ui/design-system/GdgTextarea.tsx
@@ -0,0 +1,161 @@
+'use client'
+
+import {
+ forwardRef,
+ useCallback,
+ useEffect,
+ useId,
+ useRef,
+ type ChangeEvent,
+ type TextareaHTMLAttributes
+} from 'react'
+import { cn } from '@/utils/cn'
+
+export const GDG_TEXTAREA_DEVICES = ['pc', 'mobile'] as const
+export const GDG_TEXTAREA_STATES = ['default', 'error', 'disabled'] as const
+
+export interface GdgTextareaProps extends TextareaHTMLAttributes {
+ device?: (typeof GDG_TEXTAREA_DEVICES)[number]
+ state?: 'default' | 'error' | 'disabled'
+ label?: string
+ helperText?: string
+ errorText?: string
+ fullWidth?: boolean
+}
+
+const WIDTH_CLASS: Record<'pc' | 'mobile', string> = {
+ pc: 'w-[550px]',
+ mobile: 'w-[343px]'
+}
+
+const TA_STATE: Record<
+ 'default' | 'error' | 'disabled',
+ { wrapper: string; input: string; helper: string }
+> = {
+ default: {
+ wrapper: 'border-gray-800 bg-black text-white',
+ input: 'placeholder:text-gray-700',
+ helper: 'text-gray-400'
+ },
+ error: {
+ wrapper: 'border-red bg-black text-white',
+ input: 'placeholder:text-gray-700',
+ helper: 'text-red'
+ },
+ disabled: {
+ wrapper: 'border-gray-100 bg-gray-100 text-white/40 cursor-not-allowed',
+ input: 'placeholder:text-gray-700',
+ helper: 'text-white/40'
+ }
+}
+
+export const GdgTextarea = forwardRef(
+ (
+ {
+ device = 'pc',
+ state = 'default',
+ label,
+ helperText,
+ errorText,
+ className,
+ style,
+ fullWidth,
+ disabled,
+ id,
+ rows = 4,
+ maxLength,
+ onChange,
+ value,
+ ...rest
+ },
+ forwardedRef
+ ) => {
+ const generatedId = useId()
+ const fieldId = id ?? generatedId
+ const computedState: 'default' | 'error' | 'disabled' = disabled ? 'disabled' : state
+
+ const textareaRef = useRef(null)
+ const mergeRefs = useCallback(
+ (node: HTMLTextAreaElement | null) => {
+ textareaRef.current = node
+ if (typeof forwardedRef === 'function') {
+ forwardedRef(node)
+ } else if (forwardedRef) {
+ forwardedRef.current = node
+ }
+ },
+ [forwardedRef]
+ )
+
+ const adjustHeight = useCallback(() => {
+ const el = textareaRef.current
+ if (!el) return
+ el.style.height = 'auto'
+ el.style.height = `${el.scrollHeight}px`
+ }, [])
+
+ useEffect(() => {
+ adjustHeight()
+ }, [adjustHeight, value, rows])
+
+ const handleChange = useCallback(
+ (event: ChangeEvent) => {
+ adjustHeight()
+ onChange?.(event)
+ },
+ [adjustHeight, onChange]
+ )
+
+ const currentLength = typeof value === 'string' ? value.length : 0
+
+ return (
+
+ {label && (
+
+ {label}
+
+ )}
+
+
+ {maxLength && (
+
+ {currentLength}/{maxLength}
+
+ )}
+
+ {(helperText || errorText) && (
+
+ {computedState === 'error' ? (errorText ?? helperText) : helperText}
+
+ )}
+
+ )
+ }
+)
+
+GdgTextarea.displayName = 'GdgTextarea'
diff --git a/src/components/ui/design-system/controlMeta.ts b/src/components/ui/design-system/controlMeta.ts
new file mode 100644
index 0000000..3e39c79
--- /dev/null
+++ b/src/components/ui/design-system/controlMeta.ts
@@ -0,0 +1,77 @@
+export type Device = 'pc' | 'mobile'
+
+export const GDG_PC_NARROW_WIDTHS = ['small', 'medium', 'oneThird', 'twoThirds', 'full'] as const
+export const GDG_PC_WIDE_WIDTHS = ['mini', 'small', 'quarter', 'medium', 'full'] as const
+export const GDG_MOBILE_WIDTHS = ['small', 'medium', 'twoThirds', 'full'] as const
+
+export type PcWidthVariant = 'narrow' | 'wide'
+export type PcNarrowWidthToken = (typeof GDG_PC_NARROW_WIDTHS)[number]
+export type PcWideWidthToken = (typeof GDG_PC_WIDE_WIDTHS)[number]
+export type MobileWidthToken = (typeof GDG_MOBILE_WIDTHS)[number]
+export type WidthToken = PcNarrowWidthToken | PcWideWidthToken | MobileWidthToken
+export type SizeToken = 'default' | 'mini'
+
+export const PC_WIDTH_META = {
+ narrow: {
+ small: 'w-30.5',
+ medium: 'w-66.25',
+ oneThird: 'w-43.25',
+ twoThirds: 'w-102',
+ full: 'w-137.5'
+ },
+ wide: {
+ mini: 'w-21.5',
+ small: 'w-31.125',
+ quarter: 'w-66.25',
+ medium: 'w-137.5',
+ full: 'w-280'
+ }
+} as const
+
+export const MOBILE_WIDTH_META = {
+ small: 'w-27.25',
+ medium: 'w-42',
+ twoThirds: 'w-56.5',
+ full: 'w-85.75'
+} as const
+
+export const isWideOnlyWidth = (size: WidthToken): boolean => size === 'mini' || size === 'quarter'
+
+export const getPcWidthClass = (size: WidthToken, variant: PcWidthVariant = 'narrow') => {
+ const map = PC_WIDTH_META[variant] as Record
+ return map[size as keyof typeof map] ?? map.full
+}
+
+export const getMobileWidthClass = (size: WidthToken) => {
+ const map = MOBILE_WIDTH_META as Record
+ return map[size as keyof typeof map] ?? map.full
+}
+
+export type ControlMeta = { height: string; padding: string; text: string }
+export type ControlMetaMap = Partial> & { default: ControlMeta }
+
+export const CONTROL_META: Record = {
+ pc: {
+ default: { height: 'h-11', padding: 'px-5', text: 'typo-pc-b2' },
+ mini: { height: 'h-4.5', padding: 'px-3', text: 'typo-pc-c1' }
+ },
+ mobile: {
+ default: { height: 'h-11', padding: 'px-4', text: 'typo-m-b3' }
+ }
+}
+
+export const getControlMeta = (device: Device, size: WidthToken): ControlMeta =>
+ CONTROL_META[device][size] ?? CONTROL_META[device].default
+
+export const getPlaceholderTypoByText = (textTypo: string): string => {
+ switch (textTypo) {
+ case 'typo-pc-b2':
+ return 'placeholder:typo-pc-b2'
+ case 'typo-pc-c1':
+ return 'placeholder:typo-pc-c1'
+ case 'typo-m-b3':
+ return 'placeholder:typo-m-b3'
+ default:
+ return ''
+ }
+}
diff --git a/src/components/ui/design-system/index.ts b/src/components/ui/design-system/index.ts
new file mode 100644
index 0000000..437b811
--- /dev/null
+++ b/src/components/ui/design-system/index.ts
@@ -0,0 +1,30 @@
+export { GdgButton } from './GdgButton'
+export type { GdgButtonProps } from './GdgButton'
+export { GdgSegmentedButton } from './GdgSegmentedButton'
+export type { GdgSegmentedButtonProps } from './GdgSegmentedButton'
+export { GdgTag } from './GdgTag'
+export type { GdgTagProps, GdgTagVariant, GdgTagDevice } from './GdgTag'
+export { GdgCheckbox } from './GdgCheckbox'
+export type { GdgCheckboxProps } from './GdgCheckbox'
+export { GdgRadio } from './GdgRadio'
+export type { GdgRadioProps } from './GdgRadio'
+export { GdgInputField } from './GdgInputField'
+export type { GdgInputFieldProps } from './GdgInputField'
+export { GdgTextarea } from './GdgTextarea'
+export type { GdgTextareaProps } from './GdgTextarea'
+export { GdgSearchField } from './GdgSearchField'
+export type { GdgSearchFieldProps } from './GdgSearchField'
+export { GdgFileCard, GdgUploadButton } from './GdgFileCard'
+export type { GdgFileCardProps, GdgFileCardAction, GdgUploadButtonProps } from './GdgFileCard'
+export { GdgColorTag } from './GdgColorTag'
+export type { GdgColorTagProps, GdgTagColor, GdgTagFill, GdgTagSize } from './GdgColorTag'
+export { GdgDropdown } from './GdgDropdown'
+export type { GdgDropdownProps, GdgDropdownOption, GdgDropdownOptionGroup } from './GdgDropdown'
+export { GdgMajorDropdown } from './GdgMajorDropdown'
+export type { GdgMajorDropdownProps } from './GdgMajorDropdown'
+export { GdgFieldContainer } from './GdgFieldContainer'
+export type { GdgFieldContainerProps, GdgFieldStatus } from './GdgFieldContainer'
+export { GdgLogo } from './GdgLogo'
+export type { GdgLogoProps } from './GdgLogo'
+export { GdgGoogleLoginButton } from './GdgGoogleLoginButton'
+export type { GdgGoogleLoginButtonProps } from './GdgGoogleLoginButton'
\ No newline at end of file
diff --git a/src/components/ui/input/GdgInput.tsx b/src/components/ui/input/GdgInput.tsx
new file mode 100644
index 0000000..043e059
--- /dev/null
+++ b/src/components/ui/input/GdgInput.tsx
@@ -0,0 +1,76 @@
+'use client'
+
+import { Input, type InputProps } from '@nextui-org/react'
+
+type GdgInputProps = InputProps
+type ClassNameValue = string | number | undefined | null | false | ClassNameValue[]
+
+const toClassNames = (value: ClassNameValue): string[] => {
+ if (!value) return []
+ if (Array.isArray(value)) {
+ return value.flatMap((entry) => toClassNames(entry))
+ }
+ if (typeof value === 'string' || typeof value === 'number') {
+ return [String(value)]
+ }
+ return []
+}
+
+const mergeClassNames = (base: string, extra?: ClassNameValue) =>
+ [...toClassNames(base), ...toClassNames(extra)].filter(Boolean).join(' ')
+
+export function GdgInput({ classNames, radius, ...props }: GdgInputProps) {
+ return (
+
+ )
+}
diff --git a/src/components/ui/input/GdgTextarea.tsx b/src/components/ui/input/GdgTextarea.tsx
new file mode 100644
index 0000000..23fc196
--- /dev/null
+++ b/src/components/ui/input/GdgTextarea.tsx
@@ -0,0 +1,65 @@
+'use client'
+
+import { Textarea, type TextAreaProps } from '@nextui-org/react'
+
+type GdgTextareaProps = TextAreaProps
+type ClassNameValue = string | number | undefined | null | false | ClassNameValue[]
+
+const toClassNames = (value: ClassNameValue): string[] => {
+ if (!value) return []
+ if (Array.isArray(value)) {
+ return value.flatMap((entry) => toClassNames(entry))
+ }
+ if (typeof value === 'string' || typeof value === 'number') {
+ return [String(value)]
+ }
+ return []
+}
+
+const mergeClassNames = (base: string, extra?: ClassNameValue) =>
+ [...toClassNames(base), ...toClassNames(extra)].filter(Boolean).join(' ')
+
+export function GdgTextarea({ classNames, radius, ...props }: GdgTextareaProps) {
+ return (
+
+ )
+}
diff --git a/src/components/ui/input/MajorAutocomplete.tsx b/src/components/ui/input/MajorAutocomplete.tsx
new file mode 100644
index 0000000..14fbfcc
--- /dev/null
+++ b/src/components/ui/input/MajorAutocomplete.tsx
@@ -0,0 +1,12 @@
+'use client'
+
+import {
+ type GdgMajorDropdownProps,
+ GdgMajorDropdown,
+} from '@/components/ui/design-system/GdgMajorDropdown'
+
+export type MajorAutocompleteProps = GdgMajorDropdownProps
+
+export function MajorAutocomplete(props: MajorAutocompleteProps) {
+ return
+}
diff --git a/src/components/ui/input/OtpInput.jsx b/src/components/ui/input/OtpInput.tsx
similarity index 83%
rename from src/components/ui/input/OtpInput.jsx
rename to src/components/ui/input/OtpInput.tsx
index 470ed1f..067cb92 100644
--- a/src/components/ui/input/OtpInput.jsx
+++ b/src/components/ui/input/OtpInput.tsx
@@ -4,7 +4,7 @@ import { Button, InputOtp, Form } from '@nextui-org/react';
export default function OtpInput({ setOtp, label, isDisabled, onSubmitOtp, isOtpVerified }) {
useEffect(() => {
if (!isDisabled) {
- const firstInput = document.querySelector('input[name="otp"]:not([disabled])');
+ const firstInput = document.querySelector('input[name="otp"]:not([disabled])');
if (firstInput) {
firstInput.focus();
}
@@ -37,8 +37,8 @@ export default function OtpInput({ setOtp, label, isDisabled, onSubmitOtp, isOtp
/>
>
);
-}
\ No newline at end of file
+}
diff --git a/src/components/ui/input/TransparentInput.jsx b/src/components/ui/input/TransparentInput.jsx
deleted file mode 100644
index fb9b7d4..0000000
--- a/src/components/ui/input/TransparentInput.jsx
+++ /dev/null
@@ -1,47 +0,0 @@
-'use client'
-
-import { Input } from '@nextui-org/react';
-
-const TransparentInput = ({
- label,
- isRequired = false,
- placeholder = "",
- type = "text",
- name,
- value,
- onChange,
- className = "",
- autoComplete,
- inputMode,
- isDisabled,
-}) => {
- return (
-
- );
-};
-
-export default TransparentInput;
\ No newline at end of file
diff --git a/src/components/ui/input/TransparentInput.tsx b/src/components/ui/input/TransparentInput.tsx
new file mode 100644
index 0000000..41b8a56
--- /dev/null
+++ b/src/components/ui/input/TransparentInput.tsx
@@ -0,0 +1,36 @@
+'use client'
+
+import { GdgInput } from '@/components/ui/input/GdgInput';
+
+const TransparentInput = ({
+ label,
+ isRequired = false,
+ placeholder = "",
+ type = "text",
+ name,
+ value,
+ onChange,
+ className = "",
+ autoComplete,
+ inputMode,
+ isDisabled,
+}) => {
+ return (
+
+ );
+};
+
+export default TransparentInput;
\ No newline at end of file
diff --git a/src/components/ui/input/select/MultipleSelectBox.jsx b/src/components/ui/input/select/MultipleSelectBox.tsx
similarity index 94%
rename from src/components/ui/input/select/MultipleSelectBox.jsx
rename to src/components/ui/input/select/MultipleSelectBox.tsx
index e08cb9a..1415fab 100644
--- a/src/components/ui/input/select/MultipleSelectBox.jsx
+++ b/src/components/ui/input/select/MultipleSelectBox.tsx
@@ -31,7 +31,7 @@ const MultipleSelectBox = ({ label, labelVisible, options, maxSelection, selecte
onOpenChange={handleOpenChange}
classNames={{
label: '!text-white text-xl pb-3 mobile:text-lg',
- trigger: `rounded-3xl min-h-[57px] bg-[#1c1c1c] group-data-[focus=true]:bg-[#1c1c1c] data-[hover=true]:bg-[#1c1c1c]`,
+ trigger: `rounded-3xl bg-[#1c1c1c] group-data-[focus=true]:bg-[#1c1c1c] data-[hover=true]:bg-[#1c1c1c]`,
value: 'text-lg mobile:text-base',
popoverContent: 'bg-[#1c1c1c]',
innerWrapper: 'pl-2',
diff --git a/src/components/ui/input/select/Selectbox.jsx b/src/components/ui/input/select/Selectbox.tsx
similarity index 100%
rename from src/components/ui/input/select/Selectbox.jsx
rename to src/components/ui/input/select/Selectbox.tsx
diff --git a/src/components/ui/input/select/SingleSelectBox.jsx b/src/components/ui/input/select/SingleSelectBox.tsx
similarity index 76%
rename from src/components/ui/input/select/SingleSelectBox.jsx
rename to src/components/ui/input/select/SingleSelectBox.tsx
index 68ca712..211e92c 100644
--- a/src/components/ui/input/select/SingleSelectBox.jsx
+++ b/src/components/ui/input/select/SingleSelectBox.tsx
@@ -1,6 +1,16 @@
-import { useState } from 'react';
+import { type Dispatch, type SetStateAction, useState } from 'react';
import { Select, SelectItem } from '@nextui-org/react';
+type SingleSelectBoxProps = {
+ options: string[];
+ selectedValue: string;
+ setSelectedValue: Dispatch>;
+ label?: string;
+ labelVisible: boolean;
+ placeHolder: string;
+ ariaLabel: string;
+};
+
const SingleSelectBox = ({
options,
selectedValue,
@@ -9,10 +19,10 @@ const SingleSelectBox = ({
labelVisible,
placeHolder,
ariaLabel,
-}) => {
+}: SingleSelectBoxProps) => {
const [isOpen, setIsOpen] = useState(false);
- const handleOpenChange = (open) => {
+ const handleOpenChange = (open: boolean) => {
setTimeout(() => {
setIsOpen(open);
}, 80);
@@ -48,4 +58,4 @@ const SingleSelectBox = ({
);
};
-export default SingleSelectBox;
\ No newline at end of file
+export default SingleSelectBox;
diff --git a/src/components/ui/progressbar/HorizontalProgressBar.jsx b/src/components/ui/progressbar/HorizontalProgressBar.tsx
similarity index 93%
rename from src/components/ui/progressbar/HorizontalProgressBar.jsx
rename to src/components/ui/progressbar/HorizontalProgressBar.tsx
index 9a6cdce..3b6617e 100644
--- a/src/components/ui/progressbar/HorizontalProgressBar.jsx
+++ b/src/components/ui/progressbar/HorizontalProgressBar.tsx
@@ -15,10 +15,10 @@ const HorizontalProgressBar = ({ step }) => {
return (
-
+
{
= i ? "bg-red-500" : "bg-gray-500"
+ step >= i ? "bg-red-400" : "bg-gray-400"
}`}
>
✓
diff --git a/src/components/ui/progressbar/VerticalProgressBar.jsx b/src/components/ui/progressbar/VerticalProgressBar.tsx
similarity index 89%
rename from src/components/ui/progressbar/VerticalProgressBar.jsx
rename to src/components/ui/progressbar/VerticalProgressBar.tsx
index 17babce..56abf59 100644
--- a/src/components/ui/progressbar/VerticalProgressBar.jsx
+++ b/src/components/ui/progressbar/VerticalProgressBar.tsx
@@ -15,10 +15,10 @@ const VerticalProgressBar = ({ step }) => {
return (
-
+
{
= i ? "bg-red-500" : "bg-gray-500"
+ step >= i ? "bg-red-400" : "bg-gray-400"
}`}
>
✓
diff --git a/src/constant/auth.ts b/src/constant/auth.ts
new file mode 100644
index 0000000..02cf751
--- /dev/null
+++ b/src/constant/auth.ts
@@ -0,0 +1,9 @@
+export const PENDING_SIGNUP_STORAGE_KEY = 'gdgoc.pendingSignup'
+
+export interface PendingSignupPayload {
+ oauthSubject: string
+ email: string
+ name: string
+ picture?: string
+ next?: string
+}
diff --git a/src/constant/domainOptions.js b/src/constant/domainOptions.ts
similarity index 100%
rename from src/constant/domainOptions.js
rename to src/constant/domainOptions.ts
diff --git a/src/constant/expectOptions.js b/src/constant/expectOptions.ts
similarity index 100%
rename from src/constant/expectOptions.js
rename to src/constant/expectOptions.ts
diff --git a/src/constant/interestOptions.js b/src/constant/interestOptions.ts
similarity index 100%
rename from src/constant/interestOptions.js
rename to src/constant/interestOptions.ts
diff --git a/src/constant/interestsSignup.js b/src/constant/interestsSignup.ts
similarity index 100%
rename from src/constant/interestsSignup.js
rename to src/constant/interestsSignup.ts
diff --git a/src/constant/majorOptions.js b/src/constant/majorOptions.ts
similarity index 100%
rename from src/constant/majorOptions.js
rename to src/constant/majorOptions.ts
diff --git a/src/constant/reachFromOptions.js b/src/constant/reachFromOptions.ts
similarity index 100%
rename from src/constant/reachFromOptions.js
rename to src/constant/reachFromOptions.ts
diff --git a/src/constant/semesterOptions.js b/src/constant/semesterOptions.ts
similarity index 92%
rename from src/constant/semesterOptions.js
rename to src/constant/semesterOptions.ts
index fcff06c..e7454c1 100644
--- a/src/constant/semesterOptions.js
+++ b/src/constant/semesterOptions.ts
@@ -1,4 +1,5 @@
export const semesterOptions = [
+ '26-1',
'25-2',
'25-1',
'24-2',
diff --git a/src/constant/team.ts b/src/constant/team.ts
new file mode 100644
index 0000000..73459d1
--- /dev/null
+++ b/src/constant/team.ts
@@ -0,0 +1,18 @@
+export const TEAM_VALUES = ['TECH', 'PR_DESIGN', 'HR', 'BD', 'HQ'] as const
+
+export type TeamValue = (typeof TEAM_VALUES)[number]
+
+export const CORE_TEAM_VALUES: TeamValue[] = ['HR', 'BD', 'TECH', 'PR_DESIGN']
+
+export const TEAM_LABEL_MAP: Record
= {
+ TECH: 'TECH',
+ PR_DESIGN: 'PR·DESIGN',
+ HR: 'HR',
+ BD: 'BD',
+ HQ: 'HQ'
+}
+
+export const getTeamLabel = (team?: string | null): string => {
+ if (!team) return ''
+ return TEAM_LABEL_MAP[team as TeamValue] ?? team
+}
diff --git a/src/constant/wishOptions.js b/src/constant/wishOptions.ts
similarity index 100%
rename from src/constant/wishOptions.js
rename to src/constant/wishOptions.ts
diff --git a/src/context/AuthProvider.js b/src/context/AuthProvider.js
deleted file mode 100644
index e1c0cae..0000000
--- a/src/context/AuthProvider.js
+++ /dev/null
@@ -1,29 +0,0 @@
-'use client'
-
-import { createContext, useState } from 'react';
-
-const AuthContext = createContext(null);
-
-export const AuthProvider = ({ children }) => {
- const [accessToken, setAccessToken] = useState(null);
- const [user, setUser] = useState(null);
-
- const clearAuth = () => {
- setAccessToken(null);
- setUser(null);
- };
-
- return (
-
- {children}
-
- );
-};
-
-export default AuthContext;
\ No newline at end of file
diff --git a/src/context/AuthProvider.tsx b/src/context/AuthProvider.tsx
new file mode 100644
index 0000000..5a45952
--- /dev/null
+++ b/src/context/AuthProvider.tsx
@@ -0,0 +1,139 @@
+'use client'
+
+import {
+ createContext,
+ useCallback,
+ useEffect,
+ useMemo,
+ useState,
+ type Dispatch,
+ type ReactNode,
+ type SetStateAction
+} from 'react'
+import { usePathname } from 'next/navigation'
+
+import {
+ USER_STORAGE_KEY,
+ ACCESS_TOKEN_KEY,
+ REFRESH_TOKEN_KEY,
+ readStoredUser,
+ subscribeAuthStorage,
+ writeStoredUser,
+ readStoredString,
+ writeStoredString,
+ clearStoredAuth
+} from '@/lib/auth/storage'
+import { requestAccessTokenRefresh, type RefreshResponseBody } from '@/services/auth/authClient'
+import { unwrapApiResponse } from '@/utils/api/unwrap'
+
+export interface AuthUser {
+ id?: number
+ name?: string
+ email?: string
+ userRole?: string
+ team?: string | null
+ membershipStatus?: string
+ image?: string | null
+}
+
+export interface AuthContextValue {
+ user: AuthUser
+ setUser: (user: AuthUser, accessToken?: string, refreshToken?: string) => void
+ clearAuth: () => void
+}
+
+const AuthContext = createContext(null)
+
+interface AuthProviderProps {
+ children: ReactNode
+}
+
+export const AuthProvider = ({ children }: AuthProviderProps) => {
+ const pathname = usePathname()
+ const [userState, setUserState] = useState(() => readStoredUser())
+
+ useEffect(() => {
+ if (typeof window === 'undefined') return
+ return subscribeAuthStorage(({ key }) => {
+ if (key === USER_STORAGE_KEY || key === null) {
+ setUserState(readStoredUser())
+ }
+ })
+ }, [])
+
+ const setUser = useCallback((user: AuthUser, accessToken?: string, refreshToken?: string) => {
+ writeStoredUser(user)
+ if (accessToken) writeStoredString(ACCESS_TOKEN_KEY, accessToken)
+ if (refreshToken) writeStoredString(REFRESH_TOKEN_KEY, refreshToken)
+ setUserState(user)
+ }, [])
+
+ useEffect(() => {
+ let alive = true
+
+ const bootstrap = async () => {
+ // Google OIDC implicit callback 처리 중(/login#id_token=...)에는
+ // refresh 선호출을 건너뛰어 신규 유저 분기 응답 지연을 방지한다.
+ if (
+ pathname === '/login' &&
+ typeof window !== 'undefined' &&
+ window.location.hash.includes('id_token=')
+ ) {
+ return
+ }
+
+ const storedRt = readStoredString(REFRESH_TOKEN_KEY)
+ if (!storedRt) {
+ setUser(null)
+ return
+ }
+
+ try {
+ const response = await requestAccessTokenRefresh(storedRt)
+ const data = unwrapApiResponse(response.data)
+ if (!alive) return
+
+ // Support both camelCase and snake_case for token field
+ const accessToken = data?.accessToken || (data as any)?.access_token
+
+ if (data?.user && accessToken) {
+ setUser(data.user, accessToken)
+ } else {
+ console.warn('[AuthProvider] Refresh response missing data', {
+ hasUser: !!data?.user,
+ hasToken: !!accessToken
+ })
+ clearAuth()
+ }
+ } catch (err) {
+ if (alive) {
+ console.error('[AuthProvider] Bootstrap refresh failed', err)
+ clearAuth()
+ }
+ }
+ }
+
+ void bootstrap()
+ return () => {
+ alive = false
+ }
+ }, [setUser, pathname])
+
+ const clearAuth = useCallback(() => {
+ clearStoredAuth()
+ setUserState(null)
+ }, [])
+
+ const contextValue = useMemo(
+ () => ({
+ user: userState,
+ setUser,
+ clearAuth
+ }),
+ [userState, setUser, clearAuth]
+ )
+
+ return {children}
+}
+
+export default AuthContext
diff --git a/src/context/AuthorizedFetchProvider.tsx b/src/context/AuthorizedFetchProvider.tsx
new file mode 100644
index 0000000..60f48ff
--- /dev/null
+++ b/src/context/AuthorizedFetchProvider.tsx
@@ -0,0 +1,29 @@
+'use client'
+
+import { createContext, useContext, type ReactNode } from 'react'
+
+import { type AuthorizedFetcher } from '@/lib/api/authorizedFetch'
+
+const AuthorizedFetchContext = createContext(null)
+
+interface AuthorizedFetchProviderProps {
+ fetcher: AuthorizedFetcher
+ children: ReactNode
+}
+
+export const AuthorizedFetchProvider = ({
+ fetcher,
+ children
+}: AuthorizedFetchProviderProps) => (
+
+ {children}
+
+)
+
+export const useAuthorizedFetch = (): AuthorizedFetcher => {
+ const fetcher = useContext(AuthorizedFetchContext)
+ if (!fetcher) {
+ throw new Error('useAuthorizedFetch must be used within an AuthorizedFetchProvider')
+ }
+ return fetcher
+}
diff --git a/src/hooks/homecoming/useGuestbookEntries.js b/src/hooks/homecoming/useGuestbookEntries.ts
similarity index 100%
rename from src/hooks/homecoming/useGuestbookEntries.js
rename to src/hooks/homecoming/useGuestbookEntries.ts
diff --git a/src/hooks/useAuth.js b/src/hooks/useAuth.js
deleted file mode 100644
index 6d29409..0000000
--- a/src/hooks/useAuth.js
+++ /dev/null
@@ -1,6 +0,0 @@
-'use client'
-
-import { useContext } from 'react';
-import AuthContext from '@/context/AuthProvider';
-
-export const useAuth = () => useContext(AuthContext);
\ No newline at end of file
diff --git a/src/hooks/useAuth.ts b/src/hooks/useAuth.ts
new file mode 100644
index 0000000..14a3fb0
--- /dev/null
+++ b/src/hooks/useAuth.ts
@@ -0,0 +1,15 @@
+'use client';
+
+import { useContext } from 'react';
+
+import AuthContext, { AuthContextValue } from '@/context/AuthProvider';
+
+export const useAuth = (): AuthContextValue => {
+ const context = useContext(AuthContext);
+
+ if (!context) {
+ throw new Error('useAuth must be used within an AuthProvider');
+ }
+
+ return context;
+};
diff --git a/src/hooks/useAuthApi.js b/src/hooks/useAuthApi.js
deleted file mode 100644
index e44d9ed..0000000
--- a/src/hooks/useAuthApi.js
+++ /dev/null
@@ -1,55 +0,0 @@
-'use client'
-
-import axios from 'axios';
-import { useCallback } from 'react';
-import { useAuth } from '@/hooks/useAuth';
-
-const API_AUTH_URL = process.env.NEXT_PUBLIC_BASE_API_URL + '/auth';
-
-export const useAuthApi = () => {
- const { accessToken } = useAuth();
-
- // Access Token 갱신 (stable)
- const refreshAccessToken = useCallback(async () => {
- try {
- const response = await axios.post(
- `${API_AUTH_URL}/refresh`,
- {},
- {
- headers: { 'Content-Type': 'application/json' },
- withCredentials: true,
- credentials: 'include',
- }
- );
- return response;
- } catch (error) {
- if (error.response?.status === 401) {
- console.warn('리프레시 토큰 만료');
- } else {
- console.error('Access Token 갱신 오류: ', error);
- }
- throw error;
- }
- }, []);
-
- // 로그아웃
- const logout = useCallback(async () => {
- try {
- await axios.post(
- `${API_AUTH_URL}/logout`,
- {},
- {
- withCredentials: true,
- headers: {
- Authorization: `Bearer ${accessToken}`,
- },
- }
- );
- } catch (error) {
- console.error('로그아웃 요청 오류 발생:', error);
- throw error;
- }
- }, [accessToken]);
-
- return { refreshAccessToken, logout };
-};
\ No newline at end of file
diff --git a/src/hooks/useAuthApi.ts b/src/hooks/useAuthApi.ts
new file mode 100644
index 0000000..dce8b7d
--- /dev/null
+++ b/src/hooks/useAuthApi.ts
@@ -0,0 +1,43 @@
+'use client'
+
+import axios from 'axios'
+import { useCallback } from 'react'
+
+import { requestAccessTokenRefresh, requestLogout } from '@/services/auth/authClient'
+import { REFRESH_TOKEN_KEY, readStoredString } from '@/lib/auth/storage'
+
+export type RefreshAccessTokenResponse = Awaited<
+ ReturnType
+>
+
+export const useAuthApi = () => {
+ const refreshAccessToken = useCallback(async (): Promise => {
+ const storedRt = readStoredString(REFRESH_TOKEN_KEY)
+ if (!storedRt) {
+ throw new Error('리프레시 토큰이 없습니다.')
+ }
+
+ try {
+ return await requestAccessTokenRefresh(storedRt)
+ } catch (error) {
+ if (axios.isAxiosError(error) && error.response?.status === 401) {
+ console.warn('리프레시 토큰 만료')
+ } else {
+ console.error('Access Token 갱신 오류: ', error)
+ }
+ throw error
+ }
+ }, [])
+
+ const logout = useCallback(async (): Promise => {
+ const storedRt = readStoredString(REFRESH_TOKEN_KEY)
+ try {
+ await requestLogout(storedRt ?? undefined)
+ } catch (error) {
+ console.error('로그아웃 요청 오류 발생:', error)
+ throw error
+ }
+ }, [])
+
+ return { refreshAccessToken, logout }
+}
diff --git a/src/hooks/useAuthenticatedApi.js b/src/hooks/useAuthenticatedApi.js
deleted file mode 100644
index e9eeea0..0000000
--- a/src/hooks/useAuthenticatedApi.js
+++ /dev/null
@@ -1,124 +0,0 @@
-import {useCallback, useEffect, useMemo, useRef} from 'react';
-import {useRouter} from 'next/navigation';
-import axios from 'axios';
-
-import {useAuth} from './useAuth';
-import {useAuthApi} from './useAuthApi';
-
-
-export const useAuthenticatedApi = () => {
- const {accessToken, setAccessToken, clearAuth} = useAuth();
- const {refreshAccessToken, logout} = useAuthApi();
- const router = useRouter();
- const refreshPromiseRef = useRef(null);
- const accessTokenRef = useRef(accessToken);
-
- useEffect(() => {
- accessTokenRef.current = accessToken;
- }, [accessToken]);
-
- useEffect(() => {
- return () => {
- refreshPromiseRef.current = null;
- };
- }, []);
-
- const reAccessToken = useCallback(async () => {
- if (!refreshPromiseRef.current) {
- refreshPromiseRef.current = refreshAccessToken();
- }
-
- try {
- const res = await refreshPromiseRef.current;
- return res;
- } catch (error) {
- throw error;
- } finally {
- refreshPromiseRef.current = null;
- }
- }, [refreshAccessToken]);
-
- //로그인 이후 api 요청
- const apiClient = useMemo(() => {
- const client = axios.create({
- baseURL: process.env.NEXT_PUBLIC_BASE_API_URL, withCredentials: true,
- });
-
- // 요청 인터셉터
- client.interceptors.request.use((config) => {
- // FormData인지 체크
- const isFormData = typeof FormData !== 'undefined' && config.data instanceof FormData;
- if (!isFormData && !config.headers['Content-Type']) {
- config.headers['Content-Type'] = 'application/json';
- }
-
- if (accessTokenRef.current) {
- config.headers.Authorization = `Bearer ${accessTokenRef.current}`;
- }
-
- return config;
- }, (error) => Promise.reject(error),);
-
- // 응답 인터셉터
- client.interceptors.response.use((response) => response, async (error) => {
- const originalRequest = error.config;
- const status = error.response?.status;
-
- // next 후보 수집: 헤더 > sessionStorage > 현재 URL (유지)
- const pickNext = () => originalRequest?.headers?.['X-Next-Url'] || (typeof window !== 'undefined' && sessionStorage.getItem('NEXT_URL_OVERRIDE')) || (typeof window !== 'undefined' && window.location.href) || '/';
-
- if (status === 403) {
- try {
- alert('권한이 부족합니다.');
- } catch {
- }
- router.replace('/main');
- return Promise.reject(error);
- }
-
- // 401이고 아직 재시도 안 했으면: 재발급 → 원요청 재시도
- if (status === 401 && !originalRequest?._retry) {
- originalRequest._retry = true;
- try {
- const res = await reAccessToken();
- const newAccessToken = res.data.data.access_token;
-
- setAccessToken(newAccessToken);
- accessTokenRef.current = newAccessToken;
-
- originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
- return client(originalRequest);
- } catch (refreshError) {
- // 재발급 실패 → 로그인으로
- const next = pickNext();
- try {
- alert('로그인이 만료되었습니다. 재로그인 해주세요.');
- } catch {
- }
- clearAuth();
- router.replace(`/auth/signin?next=${encodeURIComponent(next)}`);
- return Promise.reject(refreshError);
- }
- }
-
- return Promise.reject(error);
- });
-
-
- return client;
- }, [setAccessToken, clearAuth, router, reAccessToken]);
-
- //로그아웃 핸들
- const handleLogout = useCallback(async () => {
- try {
- await logout();
- } catch (error) {
- console.error('로그아웃 handle 실패:', error);
- } finally {
- clearAuth();
- router.push('/auth/signin');
- }
- }, [logout, router, clearAuth]);
-
- return {apiClient, handleLogout};
-};
\ No newline at end of file
diff --git a/src/hooks/useAuthenticatedApi.ts b/src/hooks/useAuthenticatedApi.ts
new file mode 100644
index 0000000..6125758
--- /dev/null
+++ b/src/hooks/useAuthenticatedApi.ts
@@ -0,0 +1,158 @@
+'use client'
+
+import { useCallback, useEffect, useMemo, useRef } from 'react'
+import { useRouter } from 'next/navigation'
+import {
+ AxiosHeaders,
+ type AxiosHeaderValue,
+ type InternalAxiosRequestConfig
+} from 'axios'
+
+import { createAuthorizedClient, type AuthorizedRequestConfig } from '@/lib/api/authorizedClient'
+import { createAuthorizedFetch } from '@/lib/api/authorizedFetch'
+import { useAuth } from '@/hooks/useAuth'
+import { useAuthApi } from '@/hooks/useAuthApi'
+import { type RefreshResponseBody } from '@/services/auth/authClient'
+import { unwrapApiResponse } from '@/utils/api/unwrap'
+
+const headerValueToString = (value: AxiosHeaderValue | null | undefined): string | undefined => {
+ if (value === undefined || value === null || value === false) return undefined
+ if (typeof value === 'string') return value
+ if (Array.isArray(value)) return value.map(String).join(', ')
+ if (value instanceof AxiosHeaders) return value.toString()
+ return String(value)
+}
+
+const readAxiosHeaderValue = (
+ headers?: InternalAxiosRequestConfig['headers'],
+ key?: string
+): string | undefined => {
+ if (!headers || !key) return undefined
+
+ if (headers instanceof AxiosHeaders) {
+ return headerValueToString(headers.get(key))
+ }
+
+ const normalized = headers as Record
+ return headerValueToString(normalized[key])
+}
+
+const readFetchHeaderValue = (headers?: HeadersInit, key?: string): string | undefined => {
+ if (!headers || !key) return undefined
+ try {
+ const normalized = new Headers(headers)
+ return normalized.get(key) ?? undefined
+ } catch {
+ return undefined
+ }
+}
+
+type UnauthorizedContext =
+ | { originalRequest?: AuthorizedRequestConfig }
+ | { init?: RequestInit }
+ | undefined
+
+const resolveNextUrl = (ctx?: UnauthorizedContext): string => {
+ let headerNext: string | undefined
+ if (ctx && 'originalRequest' in ctx) {
+ headerNext = readAxiosHeaderValue(ctx.originalRequest?.headers, 'X-Next-Url')
+ } else if (ctx && 'init' in ctx) {
+ headerNext = readFetchHeaderValue(ctx.init?.headers, 'X-Next-Url')
+ }
+
+ const sessionNext =
+ typeof window !== 'undefined' ? sessionStorage.getItem('NEXT_URL_OVERRIDE') : null
+ const currentUrl = typeof window !== 'undefined' ? window.location.href : null
+ return headerNext || sessionNext || currentUrl || '/'
+}
+
+export const useAuthenticatedApi = () => {
+ const { setUser, clearAuth } = useAuth()
+ const { refreshAccessToken, logout } = useAuthApi()
+ const router = useRouter()
+ const refreshPromiseRef = useRef | null>(null)
+
+ useEffect(
+ () => () => {
+ refreshPromiseRef.current = null
+ },
+ []
+ )
+
+ const requestAccessTokenRefresh = useCallback(async (): Promise => {
+ if (!refreshPromiseRef.current) {
+ refreshPromiseRef.current = refreshAccessToken()
+ .then((response) => {
+ const data = unwrapApiResponse(response.data)
+ if (data?.user && data.accessToken) {
+ setUser(data.user, data.accessToken)
+ }
+ })
+ .finally(() => {
+ refreshPromiseRef.current = null
+ })
+ }
+
+ await refreshPromiseRef.current
+ }, [refreshAccessToken, setUser])
+
+ const handleForbidden = useCallback((_payload?: unknown, _ctx?: unknown) => {
+ try {
+ alert('권한이 부족합니다.')
+ } catch {
+ // ignore
+ }
+ router.replace('/')
+ }, [router])
+
+ const handleUnauthorized = useCallback(
+ (_payload?: unknown, ctx?: UnauthorizedContext) => {
+ const next = resolveNextUrl(ctx)
+ try {
+ alert('로그인이 만료되었습니다. 재로그인 해주세요.')
+ } catch {
+ // ignore
+ }
+ clearAuth()
+ router.replace(`/login?next=${encodeURIComponent(next)}`)
+ },
+ [clearAuth, router]
+ )
+
+ const apiClient = useMemo(
+ () =>
+ createAuthorizedClient({
+ baseURL: process.env.NEXT_PUBLIC_BASE_API_URL,
+ clearAuth,
+ requestAccessTokenRefresh,
+ onForbidden: handleForbidden,
+ onUnauthorized: handleUnauthorized
+ }),
+ [clearAuth, handleForbidden, handleUnauthorized, requestAccessTokenRefresh]
+ )
+
+ const authorizedFetch = useMemo(
+ () =>
+ createAuthorizedFetch({
+ baseURL: process.env.NEXT_PUBLIC_BASE_API_URL,
+ clearAuth,
+ requestAccessTokenRefresh,
+ onForbidden: handleForbidden,
+ onUnauthorized: handleUnauthorized
+ }),
+ [clearAuth, handleForbidden, handleUnauthorized, requestAccessTokenRefresh]
+ )
+
+ const handleLogout = useCallback(async () => {
+ try {
+ await logout()
+ } catch (error) {
+ console.error('로그아웃 handle 실패:', error)
+ } finally {
+ clearAuth()
+ router.push('/login')
+ }
+ }, [logout, router, clearAuth])
+
+ return { apiClient, authorizedFetch, handleLogout }
+}
diff --git a/src/hooks/usePasswordValidation.js b/src/hooks/usePasswordValidation.ts
similarity index 100%
rename from src/hooks/usePasswordValidation.js
rename to src/hooks/usePasswordValidation.ts
diff --git a/src/lib/api/authorizedClient.ts b/src/lib/api/authorizedClient.ts
new file mode 100644
index 0000000..04992a1
--- /dev/null
+++ b/src/lib/api/authorizedClient.ts
@@ -0,0 +1,98 @@
+'use client'
+
+import axios, {
+ AxiosHeaders,
+ type AxiosInstance,
+ type InternalAxiosRequestConfig
+} from 'axios'
+
+import { ACCESS_TOKEN_KEY, readStoredString } from '@/lib/auth/storage'
+
+type RetryableRequestConfig = InternalAxiosRequestConfig & { _retry?: boolean }
+
+export type AuthorizedRequestConfig = RetryableRequestConfig
+
+const ensureAxiosHeaders = (
+ headers?: InternalAxiosRequestConfig['headers']
+): AxiosHeaders => {
+ if (headers instanceof AxiosHeaders) {
+ return headers
+ }
+ return new AxiosHeaders(headers)
+}
+
+export interface AuthorizedClientOptions {
+ baseURL?: string
+ clearAuth: () => void
+ requestAccessTokenRefresh: () => Promise
+ onForbidden?: (error: unknown, ctx: { originalRequest?: RetryableRequestConfig }) => void
+ onUnauthorized?: (error: unknown, ctx: { originalRequest?: RetryableRequestConfig }) => void
+}
+
+export const createAuthorizedClient = ({
+ baseURL,
+ clearAuth,
+ requestAccessTokenRefresh,
+ onForbidden,
+ onUnauthorized
+}: AuthorizedClientOptions): AxiosInstance => {
+ const client = axios.create({
+ baseURL,
+ withCredentials: true
+ })
+
+ client.interceptors.request.use(
+ async (config) => {
+ const headers = ensureAxiosHeaders(config.headers)
+ const isFormData = typeof FormData !== 'undefined' && config.data instanceof FormData
+
+ if (!isFormData && !headers.has('Content-Type')) {
+ headers.set('Content-Type', 'application/json')
+ }
+
+ const token = readStoredString(ACCESS_TOKEN_KEY)
+ if (token) {
+ headers.set('Authorization', `Bearer ${token}`)
+ }
+
+ config.headers = headers
+ return config
+ },
+ (error) => Promise.reject(error)
+ )
+
+ client.interceptors.response.use(
+ (response) => response,
+ async (error) => {
+ if (!axios.isAxiosError(error)) {
+ return Promise.reject(error)
+ }
+
+ const originalRequest = error.config as RetryableRequestConfig | undefined
+ const status = error.response?.status
+
+ if (status === 401 && originalRequest && !originalRequest._retry) {
+ originalRequest._retry = true
+ try {
+ await requestAccessTokenRefresh()
+ return client(originalRequest)
+ } catch (refreshError) {
+ clearAuth()
+ onUnauthorized?.(refreshError, { originalRequest })
+ return Promise.reject(refreshError)
+ }
+ }
+
+ if (status === 403) {
+ onForbidden?.(error, { originalRequest })
+ } else if (status === 401) {
+ clearAuth()
+ onUnauthorized?.(error, { originalRequest })
+ }
+
+ return Promise.reject(error)
+ }
+ )
+
+ return client
+}
diff --git a/src/lib/api/authorizedFetch.ts b/src/lib/api/authorizedFetch.ts
new file mode 100644
index 0000000..bf4f083
--- /dev/null
+++ b/src/lib/api/authorizedFetch.ts
@@ -0,0 +1,107 @@
+'use client'
+
+export type AuthorizedFetcher = (
+ input: RequestInfo | URL,
+ init?: RequestInit
+) => Promise
+
+interface FetchContext {
+ input: RequestInfo | URL
+ init?: RequestInit
+}
+
+export interface AuthorizedFetchOptions {
+ baseURL?: string
+ clearAuth: () => void
+ requestAccessTokenRefresh: () => Promise
+ onForbidden?: (response: Response, ctx: FetchContext) => void
+ onUnauthorized?: (response: Response | unknown, ctx: FetchContext) => void
+}
+
+const isAbsoluteUrl = (value: string): boolean => /^[a-zA-Z][a-zA-Z\d+\-.]*:/.test(value)
+
+const resolveUrl = (
+ input: RequestInfo | URL,
+ baseURL?: string
+): RequestInfo | URL => {
+ if (!baseURL || typeof input !== 'string') {
+ return input
+ }
+ if (isAbsoluteUrl(input) || input.startsWith('//')) {
+ return input
+ }
+ const normalizedBase = baseURL.replace(/\/+$/, '')
+ const normalizedPath = input.replace(/^\/+/, '')
+ return `${normalizedBase}/${normalizedPath}`
+}
+
+const cloneInit = (init?: RequestInit): RequestInit & { headers: Headers } => {
+ const headers = new Headers(init?.headers)
+ return {
+ ...init,
+ headers
+ }
+}
+
+const makeRequest = (
+ input: RequestInfo | URL,
+ init: RequestInit & { headers: Headers }
+): { target: RequestInfo | URL; options: RequestInit } => {
+ return {
+ target: input,
+ options: {
+ ...init,
+ credentials: init.credentials ?? 'include'
+ }
+ }
+}
+
+const buildError = (response: Response): Error => {
+ const err = new Error(`Request failed with status ${response.status}`)
+ return err
+}
+
+export const createAuthorizedFetch = ({
+ baseURL,
+ clearAuth,
+ requestAccessTokenRefresh,
+ onForbidden,
+ onUnauthorized
+}: AuthorizedFetchOptions): AuthorizedFetcher => {
+ return async (input, init) => {
+ const resolvedInput = resolveUrl(input, baseURL)
+ const context: FetchContext = { input, init }
+ const initialInit = cloneInit(init)
+
+ const dispatch = async (): Promise => {
+ const { target, options } = makeRequest(resolvedInput, initialInit)
+ return fetch(target, options)
+ }
+
+ let response = await dispatch()
+
+ if (response.status === 401) {
+ try {
+ await requestAccessTokenRefresh()
+ response = await dispatch()
+ } catch (refreshError) {
+ clearAuth()
+ onUnauthorized?.(refreshError, context)
+ throw refreshError
+ }
+ }
+
+ if (response.status === 403) {
+ onForbidden?.(response, context)
+ throw buildError(response)
+ }
+
+ if (response.status === 401) {
+ clearAuth()
+ onUnauthorized?.(response, context)
+ throw buildError(response)
+ }
+
+ return response
+ }
+}
diff --git a/src/lib/auth/storage.ts b/src/lib/auth/storage.ts
new file mode 100644
index 0000000..b8a7b2f
--- /dev/null
+++ b/src/lib/auth/storage.ts
@@ -0,0 +1,91 @@
+'use client'
+
+export type StoredAuthUser = object | null
+
+export const USER_STORAGE_KEY = 'gdgoc.user'
+export const ACCESS_TOKEN_KEY = 'gdgoc.at'
+export const REFRESH_TOKEN_KEY = 'gdgoc.rt'
+
+const getLocalStorage = (): Storage | null => {
+ if (typeof window === 'undefined') return null
+ try {
+ return window.localStorage
+ } catch {
+ return null
+ }
+}
+
+export const readStoredString = (key: string): string | null => {
+ const storage = getLocalStorage()
+ if (!storage) return null
+ try {
+ return storage.getItem(key)
+ } catch {
+ return null
+ }
+}
+
+export const writeStoredString = (key: string, value: string | null): void => {
+ const storage = getLocalStorage()
+ if (!storage) return
+ try {
+ if (value) {
+ storage.setItem(key, value)
+ } else {
+ storage.removeItem(key)
+ }
+ } catch {
+ // ignore write failure
+ }
+}
+
+export const readStoredUser = >(): T | null => {
+ const raw = readStoredString(USER_STORAGE_KEY)
+ if (!raw) return null
+ try {
+ return JSON.parse(raw) as T
+ } catch {
+ return null
+ }
+}
+
+export const writeStoredUser = (user: T | null): void => {
+ const storage = getLocalStorage()
+ if (!storage) return
+ try {
+ if (user) {
+ storage.setItem(USER_STORAGE_KEY, JSON.stringify(user))
+ } else {
+ storage.removeItem(USER_STORAGE_KEY)
+ }
+ } catch {
+ // ignore write failure
+ }
+}
+
+export const clearStoredAuth = (): void => {
+ writeStoredUser(null)
+ writeStoredString(ACCESS_TOKEN_KEY, null)
+ writeStoredString(REFRESH_TOKEN_KEY, null)
+}
+
+type StorageListener = (payload: { key: string | null; newValue: string | null }) => void
+
+export const subscribeAuthStorage = (listener: StorageListener): (() => void) => {
+ if (typeof window === 'undefined') return () => {}
+
+ const handler = (event: StorageEvent): void => {
+ if (event.storageArea !== window.localStorage) return
+ if (
+ event.key === USER_STORAGE_KEY ||
+ event.key === ACCESS_TOKEN_KEY ||
+ event.key === REFRESH_TOKEN_KEY ||
+ event.key === null
+ ) {
+ listener({ key: event.key, newValue: event.newValue })
+ }
+ }
+
+ window.addEventListener('storage', handler)
+ return () => window.removeEventListener('storage', handler)
+}
diff --git a/src/mock/events.js b/src/mock/events.ts
similarity index 100%
rename from src/mock/events.js
rename to src/mock/events.ts
diff --git a/src/mock/notices.js b/src/mock/notices.js
deleted file mode 100644
index 7b641fe..0000000
--- a/src/mock/notices.js
+++ /dev/null
@@ -1,90 +0,0 @@
-'use client';
-
-import buildwithai from '@public/images/activity/buildwithai.png';
-import seminar from '@public/images/activity/seminar.jpg';
-import conf from '@public/images/activity/conf.jpg';
-
-// 카테고리: notice | event | project
-// 상태: ongoing | closed
-export const notices = [
- {
- id: 'n-1',
- title: 'GDGoC INHA에서 진행중인 프로젝트 둘러보기',
- summary: '현재 진행 중인 웹/앱 프로젝트를 한눈에 확인하고 팀에 합류하세요.',
- category: 'notice',
- status: 'ongoing',
- image: buildwithai,
- tags: ['공지'],
- details: {
- period: '상시',
- recruitment: '-',
- schedule: '-',
- location: '-',
- link: '-'
- }
- },
- {
- id: 'n-2',
- title: 'GDGoC 25-2 신입 멤버 모집: 배우고, 만들고, 함께 성장해요! 🚀',
- summary: '인하대학교 유일의 Google 공식 IT 커뮤니티에서 새로운 멤버를 모집합니다.',
- category: 'notice',
- status: 'ongoing',
- image: buildwithai,
- tags: ['공지'],
- md: `인하대학교 유일의 Google 공식 IT 커뮤니티, **GDGoC INHA**에서 새로운 멤버를 모집합니다!\n\n개발 공부만으로는 뭔가 부족하다고 느끼셨나요?\nGDGoC에서는 **스터디로 배우고 → 프로젝트로 직접 만들고 → 해커톤과 연합 활동으로 성장**하는 경험까지, 한 학기 동안 차곡차곡 쌓아갈 수 있습니다.\n\n---\n\n## 🏷️ 우리가 함께하는 활동들\n\n### 1. 기초부터 차근차근, 정규 스터디 📚\n처음이라도 괜찮습니다. Python, 데이터 분석, AI, UX/UI 등 다양한 주제를 다루며, 팀원들과 같이 배우고 토론합니다.\n- 학기 중 두 차례 진행\n- 인프런 강의 지원 + 협력 학습\n\n### 2. 배운 걸 직접 활용하는 포트폴리오 프로젝트 💻\n한 학기 동안 팀을 이루어 **아이디어 기획 → 개발 → 배포 → 발표**까지!\n실제 서비스 개발 과정을 경험하며, **내 손으로 만든 결과물**을 남길 수 있습니다.\n\n### 3. 도전으로 성장하는 해커톤 🚀\n다양한 분야의 사람들과 짧은 시간에 함께 몰입해 아이디어를 구현하며 성장합니다.\n- DevSprint: 구글 스프린트 방식을 적용한 집중 개발 프로젝트\n- 글로벌/연합 해커톤: Solution Challenge, GreenTech Globalthon 등\n- 스타트업 비즈니스 솔루션 콘테스트: 개발자뿐 아니라 **창업·비즈니스·데이터**에 관심 있는 사람들이 함께 참여해, 문제 해결과 **사업화 아이디어**를 함께 만들어가는 특별한 기회\n\n---\n\n## 🏷️ GDGoC INHA만의 강점\n\n- ✅ **다양한 연합 네트워킹** – 인천내 대학 연합 ‘뭉(Moong)’, 타 GDGoC 지부, AIESEC·아이바스 등과의 협업으로 더 넓은 무대에서 사람들과 연결!\n- ✅ **내부 성장 프로그램** – 정규 스터디, 포트폴리오 프로젝트, 해커톤으로 실력+협업+커뮤니티 활동까지 한 번에!\n- ✅ **든든한 커뮤니티** – MT, e스포츠 대회, 한강 나들이, 크리스마스파티 등 다양한 친목 활동으로 언제든 함께할 동료!\n\n---\n\n## 🏷️ 우리는 이런 분을 기다립니다!\n\n- 개발자 혹은 개발에 관심 있는 분\n- 서비스 기획, UX/UI 디자인, 마케팅 등 다양한 분야에서 함께할 분 *(전공 무관, 이미 40여개 학과가 활동 중)*\n- 앞으로의 프로젝트에서 함께할 든든한 팀원을 찾고 싶은 분\n- 혼자보다는 함께 성장하고 싶은 분\n- 다양한 사람들과 네트워킹하며 시너지를 내고 싶은 분\n\n> ⭐ **Core Member(운영진) 모집**도 곧 시작됩니다. 관심 있는 분들은 눈여겨봐주세요!\n\n---\n\n## 📍 모집 정보\n- **집중 모집 기간**: 2025.08.29(금) ~ 2025.09.15(월)\n- **모집 링크**: https://gdgocinha.com/recruit\n- **개강 총회**: 2025.09.16(화) ✨필참✨\n- **동아리 방**: 5동 021\n\n### 문의\n- 회장: 박우찬 010-2087-1816\n- 부회장: 김정훈 010-4540-1432\n- 부회장: 최준빈 010-4252-8910\n\n- 인스타그램: @gdgoc.inha\n- 오픈채팅방 문의: https://open.kakao.com/o/sJCx9HNh\n- 더 알아보기: https://info.gdgocinha.com\n`,
- details: {
- period: '2025.08.29 ~ 2025.09.15',
- recruitment: '학부 재학생 누구나',
- schedule: '개강 총회 2025.09.16(화)',
- location: '인하대학교 5동 021',
- link: 'https://gdgocinha.com/recruit'
- }
- },
- {
- id: 'e-1',
- title: '정기 세미나 안내',
- summary: 'AI/웹 개발 주제로 진행하는 내부 세미나에 참여하세요.',
- category: 'event',
- status: 'ongoing',
- image: seminar,
- tags: ['행사'],
- details: {
- period: '2025.09.20',
- recruitment: '선착순 50명',
- schedule: '토 14:00~17:00',
- location: '인하대학교',
- link: 'https://forms.gle/'
- }
- },
- {
- id: 'p-1',
- title: '하우미 프로젝트',
- summary: '1인 가구를 위한 AI 인테리어 스타일링 서비스',
- category: 'project',
- status: 'closed',
- image: conf,
- tags: ['프로젝트'],
- details: {
- period: '2025.06 ~ 진행중',
- recruitment: '디자이너 1, FE 1',
- schedule: '주 1회',
- location: '인하대학교',
- link: '-'
- }
- }
-];
-
-export const NOTICE_CATEGORIES = [
- { key: 'all', label: '전체' },
- { key: 'notice', label: '공지' },
- { key: 'event', label: '행사' },
- { key: 'project', label: '프로젝트' },
-];
-
-export const NOTICE_STATUSES = [
- { key: 'all', label: '전체' },
- { key: 'ongoing', label: '진행중' },
- { key: 'closed', label: '종료' },
-];
-
-
diff --git a/src/mock/studyMock.js b/src/mock/studyMock.ts
similarity index 90%
rename from src/mock/studyMock.js
rename to src/mock/studyMock.ts
index 3d5f826..d7532ea 100644
--- a/src/mock/studyMock.js
+++ b/src/mock/studyMock.ts
@@ -2,10 +2,10 @@
* 스터디 리스트 정보를 불러옵니다.
* @url URL/study?page=1
* @method GET
- * @param {number} page - 페이지 번호. (필수)
- * @param {string} status - 스터디 상태, 예: "RECRUITING". (선택)
- * @param {string} creatorType - 생성자 유형, 예: "GDGOC". (선택)
- * @returns {object} studyList 배열과 meta 정보(page, pageCount)를 반환.
+ * @param page - 페이지 번호. (필수)
+ * @param status - 스터디 상태, 예: "RECRUITING". (선택)
+ * @param creatorType - 생성자 유형, 예: "GDGOC". (선택)
+ * @returns studyList 배열과 meta 정보(page, pageCount)를 반환.
*/
// GDGOC
const getStudiesGDGOC = {
@@ -79,8 +79,8 @@ const getStudiesPERSONAL = {
* 스터디 상세 정보를 불러옵니다.
* @url URL/study/{studyId}
* @method GET
- * @param {number} studyId - 스터디 ID. (필수)
- * @returns {object} 스터디 상세 정보와 meta 데이터를 반환.
+ * @param studyId - 스터디 ID. (필수)
+ * @returns 스터디 상세 정보와 meta 데이터를 반환.
*/
const getStudyDetails = {
"data": {
@@ -125,7 +125,7 @@ const getStudyDetails = {
* expectedPlace: "정석학술정보도서관 지하1층" (선택)
* image: {object} (필수)
* }
- * @returns {object} 등록 결과 데이터와 meta 정보를 반환.
+ * @returns 등록 결과 데이터와 meta 정보를 반환.
*/
const createStudyResult = {
"data": null,
@@ -140,7 +140,7 @@ const createStudyResult = {
* introduce: "저는 사실 엄청 멋있는 사람입니다!", (필수)
* activityTime: "수요일만 아니면 다 5시 이후로 가능!" (필수)
* }
- * @returns {object} 신청 결과 데이터와 meta 정보를 반환.
+ * @returns 신청 결과 데이터와 meta 정보를 반환.
*/
const applyStudyResult = {
"data": null,
@@ -151,7 +151,7 @@ const applyStudyResult = {
* [스터디 관리] 모집 중인 스터디와 모집 완료된 스터디를 나누어 조회합니다.
* @url URL/study/me
* @method GET
- * @returns {object} recruiting과 recruited 배열 및 meta 정보를 반환.
+ * @returns recruiting과 recruited 배열 및 meta 정보를 반환.
*/
const getCreatedStudiesByStatus = {
"data": {
@@ -179,9 +179,9 @@ const getCreatedStudiesByStatus = {
* [스터디 관리] 특정 스터디의 지원자 목록을 조회합니다.
* @url /study/{studyId}/attendee?page=1
* @method GET
- * @param {number} studyId - 스터디 ID. (필수)
- * @param {number} page - 페이지 번호. (선택)
- * @returns {object} attendees 배열과 meta 정보(page, pageCount)를 반환.
+ * @param studyId - 스터디 ID. (필수)
+ * @param page - 페이지 번호. (선택)
+ * @returns attendees 배열과 meta 정보(page, pageCount)를 반환.
*/
const getAttendeesByStudyId = {
"data": {
@@ -212,9 +212,9 @@ const getAttendeesByStudyId = {
* [스터디 관리] 특정 스터디의 특정 지원자 지원서 상세 정보를 조회합니다.
* @url /study/{studyId}/attendee/{attendeeId}
* @method GET
- * @param {number} studyId - 스터디 ID. (필수)
- * @param {number} attendeeId - 지원자 ID. (필수)
- * @returns {object} 지원자 상세 정보를 반환.
+ * @param studyId - 스터디 ID. (필수)
+ * @param attendeeId - 지원자 ID. (필수)
+ * @returns 지원자 상세 정보를 반환.
*/
const getAttendeeDetailsByStudyId = {
"data": {
@@ -244,7 +244,7 @@ const getAttendeeDetailsByStudyId = {
* }
* ]
* }
- * @returns {object} 상태 업데이트 결과와 meta 정보를 반환.
+ * @returns 상태 업데이트 결과와 meta 정보를 반환.
*/
const updateAttendeeStatusResult = {
"data": null,
@@ -255,7 +255,7 @@ const updateAttendeeStatusResult = {
* [스터디 관리] 내가 신청한 스터디 지원 결과를 조회합니다.
* @url /study/attendee/result
* @method GET
- * @returns {object} recruiting과 recruited 배열 및 meta 정보를 반환.
+ * @returns recruiting과 recruited 배열 및 meta 정보를 반환.
*/
const getMyStudyApplyResult = {
"data": {
diff --git a/src/mock/users.js b/src/mock/users.ts
similarity index 100%
rename from src/mock/users.js
rename to src/mock/users.ts
diff --git a/src/services/auth/authClient.ts b/src/services/auth/authClient.ts
new file mode 100644
index 0000000..b6bf4db
--- /dev/null
+++ b/src/services/auth/authClient.ts
@@ -0,0 +1,133 @@
+'use client'
+
+import axios, { type AxiosResponse } from 'axios'
+
+const AUTH_BASE_URL = `${process.env.NEXT_PUBLIC_BASE_API_URL ?? ''}/auth`
+
+export interface AuthUserPayload {
+ id?: number
+ name?: string
+ email?: string
+ userRole?: string
+ team?: string | null
+ membershipStatus?: string
+ image?: string | null
+}
+
+export interface LoginExistingUserResponse {
+ isNewUser: false
+ accessToken: string
+ refreshToken: string
+ user: AuthUserPayload | null
+}
+
+export interface LoginNewUserResponse {
+ isNewUser: true
+ oauthSubject: string
+ email: string
+ name: string
+ picture?: string
+}
+
+export type LoginApiResponseBody = LoginExistingUserResponse | LoginNewUserResponse
+
+export interface SignupRequestPayload {
+ oauthSubject: string
+ email: string
+ name: string
+ studentId: string
+ phoneNumber: string
+ major: string
+ image?: string
+}
+
+export interface SignupResponseBody {
+ accessToken: string
+ refreshToken: string
+ role?: string
+ membershipStatus?: string
+ user?: AuthUserPayload | null
+}
+
+export interface RefreshResponseBody {
+ data?: Record
+ accessToken: string
+ user?: AuthUserPayload | null
+}
+
+export interface DuplicateCheckResponseBody {
+ isExists: boolean
+}
+
+interface ApiEnvelope {
+ code: number
+ message: string
+ data: T
+}
+
+const defaultHeaders = { 'Content-Type': 'application/json' } as const
+const defaultConfig = {
+ headers: defaultHeaders,
+ withCredentials: true
+} as const
+
+export const loginWithGoogleIdToken = (
+ idToken: string
+): Promise> =>
+ axios.post(
+ `${AUTH_BASE_URL}/login`,
+ { idToken },
+ {
+ ...defaultConfig
+ }
+ )
+
+export const signupWithProfile = (
+ payload: SignupRequestPayload
+): Promise> =>
+ axios.post(`${AUTH_BASE_URL}/signup`, payload, {
+ ...defaultConfig
+ })
+
+export const requestAccessTokenRefresh = (
+ refreshToken: string
+): Promise> =>
+ axios.post(
+ `${AUTH_BASE_URL}/refresh`,
+ { refreshToken },
+ {
+ ...defaultConfig
+ }
+ )
+
+export const requestLogout = (refreshToken?: string): Promise> =>
+ axios.post(
+ `${AUTH_BASE_URL}/logout`,
+ { refreshToken },
+ {
+ withCredentials: true,
+ headers: defaultHeaders
+ }
+ )
+
+export const checkStudentIdDuplicated = (
+ studentId: string
+): Promise>> =>
+ axios.post(
+ `${AUTH_BASE_URL}/check/student-id`,
+ { studentId },
+ {
+ ...defaultConfig
+ }
+ )
+
+export const checkPhoneNumberDuplicated = (
+ phoneNumber: string
+): Promise>> =>
+ axios.post(
+ `${AUTH_BASE_URL}/check/phone-number`,
+ { phoneNumber },
+ {
+ ...defaultConfig
+ }
+ )
diff --git a/src/services/auth/signin/custom/CustomAuthApi.js b/src/services/auth/signin/custom/CustomAuthApi.ts
similarity index 95%
rename from src/services/auth/signin/custom/CustomAuthApi.js
rename to src/services/auth/signin/custom/CustomAuthApi.ts
index 9262055..57952e0 100644
--- a/src/services/auth/signin/custom/CustomAuthApi.js
+++ b/src/services/auth/signin/custom/CustomAuthApi.ts
@@ -12,7 +12,6 @@ export const login = async (email, password) => {
{
headers: { 'Content-Type': 'application/json' },
withCredentials: true,
- credentials: 'include',
}
);
return response;
diff --git a/src/services/auth/signin/custom/CustomRegister.js b/src/services/auth/signin/custom/CustomRegister.ts
similarity index 100%
rename from src/services/auth/signin/custom/CustomRegister.js
rename to src/services/auth/signin/custom/CustomRegister.ts
diff --git a/src/services/auth/signin/google/GoogleAuth.js b/src/services/auth/signin/google/GoogleAuth.tsx
similarity index 74%
rename from src/services/auth/signin/google/GoogleAuth.js
rename to src/services/auth/signin/google/GoogleAuth.tsx
index 1d9536b..6da1f30 100644
--- a/src/services/auth/signin/google/GoogleAuth.js
+++ b/src/services/auth/signin/google/GoogleAuth.tsx
@@ -6,11 +6,12 @@ import { useSearchParams, useRouter } from 'next/navigation';
import Loader from '@/components/ui/common/Loader'
import { useAuth } from '@/hooks/useAuth';
+import { unwrapApiResponse } from '@/utils/api/unwrap';
import { exchangeGoogleToken } from '@/services/auth/signin/google/GoogleAuthApi';
export const GoogleAuthComponent = () => {
- const { setAccessToken } = useAuth();
+ const { setUser } = useAuth();
const searchParams = useSearchParams();
const router = useRouter();
const [isLoading, setIsLoading] = useState(true);
@@ -39,11 +40,15 @@ export const GoogleAuthComponent = () => {
exchangeGoogleToken(code)
.then((res) => {
- const data = res?.data?.data || {};
+ const data = unwrapApiResponse>(res?.data) || {};
const exists = typeof data.exists === 'boolean' ? data.exists : data.isExists;
- const { access_token, email, name } = data;
+ const email = typeof data.email === 'string' ? data.email : '';
+ const name = typeof data.name === 'string' ? data.name : '';
+ const user = (data.user ?? null) as Record | null;
if (exists) {
- setAccessToken(access_token);
+ if (user) {
+ setUser(user);
+ }
router.push(nextPath || '/main');
} else {
alert('회원 정보가 없습니다. 회원가입을 완료해주세요.');
@@ -54,10 +59,14 @@ export const GoogleAuthComponent = () => {
})
.catch(() => {
alert('구글 로그인 실패! 다시 시도해주세요.');
- router.push('/auth/signin');
+ if (nextPath) {
+ router.push(`/login?next=${encodeURIComponent(nextPath)}`);
+ } else {
+ router.push('/login');
+ }
})
.finally(() => setIsLoading(false));
- }, [searchParams, router, setAccessToken]);
+ }, [searchParams, router, setUser]);
if (isLoading) {
return ;
diff --git a/src/services/auth/signin/google/GoogleAuthApi.js b/src/services/auth/signin/google/GoogleAuthApi.ts
similarity index 95%
rename from src/services/auth/signin/google/GoogleAuthApi.js
rename to src/services/auth/signin/google/GoogleAuthApi.ts
index 939b05d..a21984f 100644
--- a/src/services/auth/signin/google/GoogleAuthApi.js
+++ b/src/services/auth/signin/google/GoogleAuthApi.ts
@@ -9,7 +9,6 @@ export const exchangeGoogleToken = async (code) => {
const response = await axios.get(`${GOOGLE_AUTH_URL}/callback?code=${code}`, {
headers: { 'Content-Type': 'application/json' },
withCredentials: true,
- credentials: 'include',
});
const cookies = response.headers['set-cookie'];
diff --git a/src/services/auth/signin/google/GoogleLogin.js b/src/services/auth/signin/google/GoogleLogin.ts
similarity index 80%
rename from src/services/auth/signin/google/GoogleLogin.js
rename to src/services/auth/signin/google/GoogleLogin.ts
index c455f71..9f65068 100644
--- a/src/services/auth/signin/google/GoogleLogin.js
+++ b/src/services/auth/signin/google/GoogleLogin.ts
@@ -2,7 +2,11 @@ const clientId = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_CLIENT_ID;
const redirectUri = process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI;
const googleLoginBaseUrl = `https://accounts.google.com/o/oauth2/auth?client_id=${clientId}&redirect_uri=${redirectUri}&response_type=code&scope=email profile`;
-const buildGoogleLoginUrl = (nextPath) => {
+interface GoogleLoginOptions {
+ next?: string;
+}
+
+const buildGoogleLoginUrl = (nextPath?: string) => {
if (!nextPath || typeof nextPath !== 'string') {
return googleLoginBaseUrl;
}
@@ -17,7 +21,7 @@ const buildGoogleLoginUrl = (nextPath) => {
};
export const GoogleLogin = () => {
- const handleGoogleLogin = ({ next } = {}) => {
+ const handleGoogleLogin = ({ next }: GoogleLoginOptions = {}) => {
window.location.href = buildGoogleLoginUrl(next);
};
diff --git a/src/styles/globals.css b/src/styles/globals.css
index 5b68287..3598d86 100644
--- a/src/styles/globals.css
+++ b/src/styles/globals.css
@@ -1,89 +1,1000 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;
+@import "tailwindcss";
-:root {
- background-color: black;
+@source "../**/*.{js,jsx,ts,tsx,mdx}";
+@source "../../node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}";
+@source "../../node_modules/@heroui/theme/dist/components/scroll-shadow.js";
+
+/* ========================================================================
+ Theme Tokens
+ ======================================================================== */
+@theme {
+ --font-pretendard: var(--font-local-pretendard, 'Pretendard Variable', 'Pretendard', sans-serif);
+ --font-google-sans-flex: var(--font-local-google-sans-flex, 'Google Sans Flex', 'Pretendard', sans-serif);
+ --font-ocra: var(--font-local-ocra, 'OCRAExtended', monospace);
+
+ --color-black: #1E1E1E;
+ --color-white: #F0F0F0;
+ --color-white-40: color-mix(in srgb, var(--color-white) 40%, transparent);
+
+ --color-red: #EA4335;
+ --color-red-400: #4F1C17;
+ --color-blue: #4285F4;
+ --color-blue-400: #1E3252;
+ --color-green: #34A853;
+ --color-green-400: #143D1F;
+ --color-yellow: #F9AB00;
+ --color-yellow-400: #493812;
+
+ --color-gray-100: #333333;
+ --color-gray-200: #424242;
+ --color-gray-300: #525252;
+ --color-gray-400: #666666;
+ --color-gray-500: #7D7D7D;
+ --color-gray-600: #979797;
+ --color-gray-700: #B2B2B2;
+ --color-gray-800: #CCCCCC;
+ --color-gray-900: #E1E1E1;
+
+ --animate-spin: spin 1s linear infinite;
+ --animate-ping: ping 1s cubic-bezier(0, 0, 0.2, 1) infinite;
+ --animate-pulse: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
+ --animate-bounce: bounce 1s infinite;
}
-@layer utilities {
- input:-webkit-autofill {
- -webkit-text-fill-color: white;
+/* Breakpoints */
+@custom-variant mobile (@media (max-width: 47.9375rem));
+@custom-variant pc (@media (min-width: 48rem));
+
+/* NextUI / HeroUI Variants for Tailwind v4 */
+@custom-variant hover (&:hover, &[data-hover=true]);
+@custom-variant focus (&:focus, &[data-focus=true]);
+@custom-variant focus-visible (&:focus-visible, &[data-focus-visible=true]);
+@custom-variant selected (&[data-selected=true], &[aria-selected=true], &[aria-checked=true]);
+@custom-variant disabled (&:disabled, &[data-disabled=true], &[aria-disabled=true]);
+@custom-variant invalid (&[data-invalid=true], &[aria-invalid=true]);
+@custom-variant pressed (&:active, &[data-pressed=true], &[aria-pressed=true]);
+@custom-variant active (&[data-active=true], &[data-state=active]);
+@custom-variant open (&[data-open=true], &[data-state=open]);
+@custom-variant closed (&[data-closed=true], &[data-state=closed]);
+
+/* ========================================================================
+ Base Layer
+ ======================================================================== */
+@layer base {
+ :root {
+ background-color: var(--color-black);
+ color: var(--color-white);
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ text-rendering: optimizeLegibility;
+ }
+
+ .dark {
+ background-color: var(--color-black);
+ color: var(--color-white);
+ }
+
+ *, ::before, ::after {
+ border-width: 0;
+ border-style: solid;
+ box-sizing: border-box;
}
}
-body{
- -ms-overflow-style: none;
+/* ========================================================================
+ Typography Utilities (Tailwind v4 @utility standard)
+ ======================================================================== */
+
+@utility typo-d1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 120px;
+ font-weight: 800;
+ line-height: 140px;
+ letter-spacing: 0;
}
+@utility typo-pc-d1 { @apply typo-d1; }
-::-webkit-scrollbar {
- display: none;
+@utility typo-d2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 80px;
+ font-weight: 800;
+ line-height: 96px;
+ letter-spacing: 0;
}
+@utility typo-pc-d2 { @apply typo-d2; }
-input {
- margin-left: 10px;
+@utility typo-h1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 48px;
+ font-weight: 800;
+ line-height: 64px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 24px;
+ font-weight: 700;
+ line-height: 34px;
+ }
+}
+@utility typo-pc-h1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 48px;
+ font-weight: 800;
+ line-height: 64px;
+ letter-spacing: 0;
}
+@utility typo-h2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 36px;
+ font-weight: 800;
+ line-height: 48px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 30px;
+ }
+}
+@utility typo-pc-h2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 36px;
+ font-weight: 800;
+ line-height: 48px;
+ letter-spacing: 0;
+}
-[data-top-scroll="true"] {
- mask-image: none !important;
+@utility typo-h3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 28px;
+ font-weight: 800;
+ line-height: 38px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 26px;
+ }
+}
+@utility typo-pc-h3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 28px;
+ font-weight: 800;
+ line-height: 38px;
+ letter-spacing: 0;
}
-[data-top-scroll="false"] {
- mask-image: none !important;
+@utility typo-h4 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 24px;
+ font-weight: 800;
+ line-height: 34px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ }
+}
+@utility typo-pc-h4 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 24px;
+ font-weight: 800;
+ line-height: 34px;
+ letter-spacing: 0;
+}
+
+@utility typo-h5 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 20px;
+ font-weight: 800;
+ line-height: 28px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ }
+}
+@utility typo-pc-h5 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 20px;
+ font-weight: 800;
+ line-height: 28px;
+ letter-spacing: 0;
+}
+
+@utility typo-s1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 30px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ }
+}
+@utility typo-pc-s1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 30px;
+ letter-spacing: 0;
+}
+
+@utility typo-s2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 26px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ }
+}
+@utility typo-pc-s2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 26px;
+ letter-spacing: 0;
+}
+
+@utility typo-s3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 12px;
+ font-weight: 700;
+ line-height: 18px;
+ }
+}
+@utility typo-pc-s3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+
+@utility typo-b1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 18px;
+ font-weight: 500;
+ line-height: 28px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 18px;
+ font-weight: 500;
+ line-height: 26px;
+ }
+}
+@utility typo-pc-b1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 18px;
+ font-weight: 500;
+ line-height: 28px;
+ letter-spacing: 0;
+}
+
+@utility typo-b2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 24px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 24px;
+ }
+}
+@utility typo-pc-b2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+
+@utility typo-b3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ }
+}
+@utility typo-pc-b3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: 0;
+}
+
+@utility typo-c1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 18px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 18px;
+ }
+}
+@utility typo-pc-c1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 18px;
+ letter-spacing: 0;
+}
+
+@utility typo-c2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 14px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 14px;
+ }
+}
+@utility typo-pc-c2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 14px;
+ letter-spacing: 0;
+}
+
+/* Mobile specific Pretendard tokens */
+@utility typo-m-h1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 24px;
+ font-weight: 700;
+ line-height: 34px;
+ letter-spacing: 0;
+}
+@utility typo-m-h2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 30px;
+ letter-spacing: 0;
+}
+@utility typo-m-h3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 26px;
+ letter-spacing: 0;
+}
+@utility typo-m-h4 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+@utility typo-m-h5 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ letter-spacing: 0;
+}
+@utility typo-m-s1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+@utility typo-m-s2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ letter-spacing: 0;
+}
+@utility typo-m-s3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 12px;
+ font-weight: 700;
+ line-height: 18px;
+ letter-spacing: 0;
+}
+@utility typo-m-b1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 18px;
+ font-weight: 500;
+ line-height: 26px;
+ letter-spacing: 0;
+}
+@utility typo-m-b2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+@utility typo-m-b3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: 0;
+}
+@utility typo-m-c1 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 18px;
+ letter-spacing: 0;
+}
+@utility typo-m-c2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 14px;
+ letter-spacing: 0;
+}
+
+/* Google Sans Flex typography tokens */
+@utility typo-gsf-d1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 128px;
+ font-weight: 800;
+ line-height: 140px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-pc-d1 { @apply typo-gsf-d1; }
+
+@utility typo-gsf-d2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 88px;
+ font-weight: 800;
+ line-height: 100px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-pc-d2 { @apply typo-gsf-d2; }
+
+@utility typo-gsf-h1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 52px;
+ font-weight: 800;
+ line-height: 64px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 26px;
+ font-weight: 700;
+ line-height: 34px;
+ }
+}
+@utility typo-gsf-pc-h1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 52px;
+ font-weight: 800;
+ line-height: 64px;
+ letter-spacing: 0;
+}
+
+@utility typo-gsf-h2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 38px;
+ font-weight: 800;
+ line-height: 48px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 22px;
+ font-weight: 700;
+ line-height: 28px;
+ }
+}
+@utility typo-gsf-pc-h2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 38px;
+ font-weight: 800;
+ line-height: 48px;
+ letter-spacing: 0;
+}
+
+@utility typo-gsf-h3 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 30px;
+ font-weight: 800;
+ line-height: 40px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 24px;
+ }
+}
+@utility typo-gsf-pc-h3 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 30px;
+ font-weight: 800;
+ line-height: 40px;
+ letter-spacing: 0;
+}
+
+@utility typo-gsf-h4 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 24px;
+ font-weight: 800;
+ line-height: 32px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 22px;
+ }
+}
+@utility typo-gsf-pc-h4 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 24px;
+ font-weight: 800;
+ line-height: 32px;
+ letter-spacing: 0;
+}
+
+@utility typo-gsf-h5 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 20px;
+ font-weight: 800;
+ line-height: 28px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ }
+}
+@utility typo-gsf-pc-h5 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 20px;
+ font-weight: 800;
+ line-height: 28px;
+ letter-spacing: 0;
+}
+
+@utility typo-gsf-s1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 30px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ }
+}
+@utility typo-gsf-pc-s1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 20px;
+ font-weight: 700;
+ line-height: 30px;
+ letter-spacing: 0;
+}
+
+@utility typo-gsf-s2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 26px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ }
+}
+@utility typo-gsf-pc-s2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 26px;
+ letter-spacing: 0;
+}
+
+@utility typo-gsf-s3 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 22px;
+ letter-spacing: 0;
+ @media (max-width: 48rem) {
+ font-size: 13px;
+ font-weight: 700;
+ line-height: 18px;
+ }
+}
+@utility typo-gsf-pc-s3 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 22px;
+ letter-spacing: 0;
+}
+
+/* Mobile specific Google Sans Flex tokens */
+@utility typo-gsf-m-h1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 26px;
+ font-weight: 700;
+ line-height: 34px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-h2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 22px;
+ font-weight: 700;
+ line-height: 28px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-h3 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 18px;
+ font-weight: 700;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-h4 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 22px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-h5 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-s1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 16px;
+ font-weight: 700;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-s2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 14px;
+ font-weight: 700;
+ line-height: 20px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-s3 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 13px;
+ font-weight: 700;
+ line-height: 18px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-b1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 18px;
+ font-weight: 500;
+ line-height: 26px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-b2 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 16px;
+ font-weight: 500;
+ line-height: 24px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-b3 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 20px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-c1 {
+ font-family: var(--font-google-sans-flex), sans-serif;
+ font-size: 12px;
+ font-weight: 500;
+ line-height: 16px;
+ letter-spacing: 0;
+}
+@utility typo-gsf-m-c2 {
+ font-family: var(--font-pretendard), sans-serif;
+ font-size: 11px;
+ font-weight: 500;
+ line-height: 14px;
+ letter-spacing: 0;
+}
+
+@utility input-autofill {
+ -webkit-text-fill-color: white;
+}
+
+/* ====================================================================
+ Layout Grid Utilities
+ ==================================================================== */
+@utility layout-grid {
+ display: grid;
+ grid-template-columns: repeat(var(--grid-columns, 12), minmax(0, 1fr));
+ column-gap: var(--grid-gutter, 20px);
+ width: var(--grid-width, auto);
+ margin-inline: var(--grid-margin-inline, 0);
+}
+
+@utility layout-grid--wide {
+ --grid-width: min(100%, 343px);
+ --grid-margin-inline: auto;
+ --grid-gutter: 8px;
+ @media (min-width: 48rem) {
+ --grid-width: min(100% - 160px, 1120px);
+ --grid-gutter: 20px;
+ }
+}
+
+@utility layout-grid--narrow {
+ --grid-width: min(100%, 343px);
+ --grid-margin-inline: auto;
+ --grid-gutter: 8px;
+ @media (min-width: 48rem) {
+ --grid-width: min(100% - 32px, 550px);
+ --grid-gutter: 20px;
+ }
+}
+
+@utility layout-grid--mobile {
+ --grid-width: min(100%, 343px);
+ --grid-margin-inline: auto;
+ --grid-gutter: 8px;
+}
+
+@utility layout-grid--wide-screen {
+ --grid-width: min(100%, 343px);
+ --grid-margin-inline: auto;
+ --grid-gutter: 8px;
+ @media (min-width: 48rem) {
+ --grid-width: min(100% - 160px, 1120px);
+ --grid-gutter: 20px;
+ }
+}
+
+@utility layout-grid--narrow-screen {
+ --grid-width: min(100%, 343px);
+ --grid-margin-inline: auto;
+ --grid-gutter: 8px;
+ @media (min-width: 48rem) {
+ --grid-width: min(100% - 32px, 550px);
+ --grid-gutter: 20px;
+ }
+}
+
+@utility layout-grid--12 { --grid-columns: 12; }
+@utility layout-grid--8 { --grid-columns: 8; }
+@utility layout-grid--4 { --grid-columns: 4; }
+@utility layout-grid--3 { --grid-columns: 3; }
+@utility layout-grid--2 { --grid-columns: 2; }
+
+/* ====================================================================
+ Grid Showcase Helpers
+ ==================================================================== */
+@utility ds-grid-panel {
+ display: grid;
+ gap: 24px;
+ @media (min-width: 75rem) {
+ grid-template-columns: minmax(0, 420px) minmax(0, 1fr);
+ align-items: flex-start;
+ }
+}
+
+@utility ds-grid-heading {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 12px;
+ align-items: center;
+}
+
+@utility ds-grid-badge {
+ border-radius: 999px;
+ border: 1px solid color-mix(in srgb, var(--color-white) 20%, transparent);
+ padding: 2px 12px;
+ font-size: 12px;
+ font-weight: 600;
+ letter-spacing: 0.1em;
+ text-transform: uppercase;
+ color: var(--color-white);
+}
+
+@utility ds-grid-card {
+ border-radius: 20px;
+ border: 1px solid color-mix(in srgb, var(--color-white) 12%, transparent);
+ background: color-mix(in srgb, var(--color-white) 4%, transparent);
+ padding: 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 16px;
+}
+
+@utility ds-grid-viewport {
+ --grid-count: 12;
+ --grid-frame-width: 80rem;
+ --grid-content-width: 70rem;
+ --grid-gutter: 20px;
+ --grid-height: 24rem;
+ --grid-column-color: color-mix(in srgb, var(--color-red) 60%, transparent);
+ border-radius: 24px;
+ border: 1px solid color-mix(in srgb, var(--color-white) 10%, transparent);
+ background: color-mix(in srgb, var(--color-white) 3%, transparent);
+ padding: clamp(16px, 3vw, 24px);
+ width: min(100%, var(--grid-frame-width));
+ margin-inline: auto;
+
+ &[data-device="mobile"] {
+ --grid-frame-width: 23.4375rem;
+ --grid-content-width: 21.4375rem;
+ --grid-gutter: 8px;
+ --grid-height: 16rem;
+ --grid-column-color: color-mix(in srgb, var(--color-blue) 60%, transparent);
+ }
+}
+
+@utility ds-grid-columns {
+ width: min(100%, var(--grid-content-width));
+ margin-inline: auto;
+ display: grid;
+ grid-template-columns: repeat(var(--grid-count), minmax(0, 1fr));
+ gap: var(--grid-gutter);
+ min-height: var(--grid-height);
+}
+
+@utility ds-grid-column {
+ border-radius: 12px;
+ background-color: var(--grid-column-color);
+}
+
+/* ====================================================================
+ Component Specific Overrides
+ ==================================================================== */
+
+/* Keep Autocomplete inner focus ring from overriding custom border styles. */
+.gdg-dropdown-trigger [data-slot="input-wrapper"] {
+ border: 0;
+ outline: 0;
+ box-shadow: none;
+ background: transparent;
+}
+
+.gdg-dropdown-trigger [data-slot="selector-button"] {
+ border: 0;
+ outline: 0;
+ box-shadow: none;
+}
+
+.gdg-dropdown-trigger[data-focus-visible="true"],
+.gdg-dropdown-trigger [data-focus-visible="true"] {
+ outline: 0;
+ box-shadow: none;
+}
+
+/* Force-disable NextUI focus ring/shadow on input-like wrappers globally. */
+[data-slot="input-wrapper"],
+[data-slot="input-wrapper"]:focus-within,
+[data-slot="input-wrapper"][data-focus="true"],
+[data-slot="input-wrapper"][data-focus-visible="true"] {
+ outline: 0;
+ box-shadow: none;
+ background: transparent;
+ border: none;
+}
+
+body {
+ -ms-overflow-style: auto;
+}
+
+input:-webkit-autofill,
+input:-webkit-autofill:hover,
+input:-webkit-autofill:focus,
+input:-webkit-autofill:active {
+ -webkit-box-shadow: 0 0 0 1000px var(--color-black) inset !important;
+ -webkit-text-fill-color: var(--color-white) !important;
+ transition: background-color 5000s ease-in-out 0s;
+}
+
+::-webkit-scrollbar {
+ display: none;
+}
+
+/* Hide native search clear button in Chrome */
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button,
+input[type="search"]::-webkit-search-results-button,
+input[type="search"]::-webkit-search-results-decoration {
+ display: none;
}
.neon-text {
- text-shadow:
- 0 0 4px currentColor,
+ text-shadow: 0 0 4px currentColor,
0 0 8px currentColor,
0 0 12px currentColor;
- animation: neon-glow 1.5s ease-in-out infinite alternate;
+ animation: neon-glow 1.5s ease-in-out infinite alternate;
}
.neon-text-sm {
- text-shadow:
- 0 0 1px currentColor,
+ text-shadow: 0 0 1px currentColor,
0 0 2px currentColor,
0 0 3px currentColor;
}
@keyframes neon-glow {
- from {
- text-shadow:
- 0 0 4px currentColor,
- 0 0 8px currentColor,
- 0 0 12px currentColor;
- }
- to {
- text-shadow:
- 0 0 6px currentColor,
- 0 0 10px currentColor,
- 0 0 15px currentColor;
- }
+ from {
+ text-shadow: 0 0 4px currentColor,
+ 0 0 8px currentColor,
+ 0 0 12px currentColor;
+ }
+ to {
+ text-shadow: 0 0 6px currentColor,
+ 0 0 10px currentColor,
+ 0 0 15px currentColor;
+ }
}
.arrow-path {
- stroke-dasharray: 1000;
- stroke-dashoffset: 1000;
+ stroke-dasharray: 1000;
+ stroke-dashoffset: 1000;
}
.no-scrollbar {
-ms-overflow-style: none; /* IE, Edge */
- scrollbar-width: none; /* Firefox */
+ scrollbar-width: none; /* Firefox */
}
.no-scrollbar::-webkit-scrollbar {
- display: none; /* Chrome, Safari */
+ display: none; /* Chrome, Safari */
}
@keyframes floatY {
- 0%, 100% { transform: translateY(0); }
- 50% { transform: translateY(-10px); }
+ 0%, 100% {
+ transform: translateY(0);
+ }
+ 50% {
+ transform: translateY(-10px);
+ }
}
.animate-floatY {
animation: floatY 1.6s ease-in-out infinite;
}
+[data-scroll-shadow]::before,
+[data-scroll-shadow]::after {
+ display: none !important;
+ mask-image: none !important;
+ box-shadow: none !important;
+}
+
+.gdg-dropdown-popover [data-top-scroll],
+.gdg-dropdown-popover [data-bottom-scroll] {
+ mask-image: none !important;
+ -webkit-mask-image: none !important;
+}
diff --git a/src/types/type-hangul-module.d.ts b/src/types/type-hangul-module.d.ts
new file mode 100644
index 0000000..049f3e8
--- /dev/null
+++ b/src/types/type-hangul-module.d.ts
@@ -0,0 +1,15 @@
+declare module 'type-hangul/src' {
+ interface TypeHangulOptions {
+ text: string
+ speed?: number
+ intervalType?: number
+ humanize?: number
+ }
+
+ interface TypeHangulApi {
+ type: (selector: string, options: TypeHangulOptions) => void
+ }
+
+ const TypeHangul: TypeHangulApi
+ export default TypeHangul
+}
diff --git a/src/types/type-hangul.d.ts b/src/types/type-hangul.d.ts
new file mode 100644
index 0000000..7e33d0c
--- /dev/null
+++ b/src/types/type-hangul.d.ts
@@ -0,0 +1,9 @@
+export {}
+
+declare global {
+ interface Window {
+ TypeHangul?: {
+ type: (selector: string, options: Record) => void
+ }
+ }
+}
diff --git a/src/utils/api/unwrap.ts b/src/utils/api/unwrap.ts
new file mode 100644
index 0000000..ddec27b
--- /dev/null
+++ b/src/utils/api/unwrap.ts
@@ -0,0 +1,27 @@
+const WRAPPER_KEYS = ['data', 'result', 'payload', 'content'] as const
+
+type WrapperKey = (typeof WRAPPER_KEYS)[number]
+
+const isRecord = (value: unknown): value is Record =>
+ typeof value === 'object' && value !== null
+
+export const unwrapApiResponse = (payload: unknown): T => {
+ let current: unknown = payload
+
+ while (isRecord(current)) {
+ let unwrapped = false
+ for (const key of WRAPPER_KEYS) {
+ if (key in current && current[key as WrapperKey] !== undefined) {
+ current = current[key as WrapperKey]
+ unwrapped = true
+ break
+ }
+ }
+
+ if (!unwrapped) {
+ break
+ }
+ }
+
+ return current as T
+}
diff --git a/src/utils/auth/tokens.ts b/src/utils/auth/tokens.ts
new file mode 100644
index 0000000..750bc2a
--- /dev/null
+++ b/src/utils/auth/tokens.ts
@@ -0,0 +1,35 @@
+'use client'
+
+const NESTED_KEYS = ['data', 'result', 'payload', 'content']
+
+type TokenCarrier = unknown
+
+const toRecord = (input: unknown): Record | null =>
+ typeof input === 'object' && input !== null ? (input as Record) : null
+
+const pickToken = (carrier: TokenCarrier, keys: string[]): string | undefined => {
+ const record = toRecord(carrier)
+ if (!record) {
+ return undefined
+ }
+
+ for (const key of keys) {
+ const value = record[key]
+ if (typeof value === 'string' && value.length > 0) {
+ return value
+ }
+ }
+
+ for (const nestedKey of NESTED_KEYS) {
+ const nestedValue = record[nestedKey]
+ const nestedToken = pickToken(nestedValue, keys)
+ if (nestedToken) {
+ return nestedToken
+ }
+ }
+
+ return undefined
+}
+
+export const extractAccessToken = (carrier: TokenCarrier): string | undefined =>
+ pickToken(carrier, ['accessToken', 'access_token'])
diff --git a/src/utils/cn.ts b/src/utils/cn.ts
new file mode 100644
index 0000000..d32b0fe
--- /dev/null
+++ b/src/utils/cn.ts
@@ -0,0 +1,6 @@
+import { type ClassValue, clsx } from 'clsx'
+import { twMerge } from 'tailwind-merge'
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/src/utils/date.ts b/src/utils/date.ts
new file mode 100644
index 0000000..b1ad336
--- /dev/null
+++ b/src/utils/date.ts
@@ -0,0 +1,22 @@
+export const formatDateInput = (value: string): string => {
+ const sanitized = value.replace(/[^0-9.]/g, '')
+ const digits = sanitized.replace(/\./g, '').slice(0, 8)
+
+ let formatted = digits
+ if (digits.length > 4) {
+ formatted = `${digits.slice(0, 4)}.${digits.slice(4, Math.min(digits.length, 6))}`
+ }
+ if (digits.length > 6) {
+ formatted = `${digits.slice(0, 4)}.${digits.slice(4, 6)}.${digits.slice(6)}`
+ }
+
+ // Allow manual "." while typing at valid boundaries.
+ if (sanitized.endsWith('.')) {
+ if (digits.length === 4) return `${digits}.`
+ if (digits.length >= 5 && digits.length <= 6) {
+ return `${digits.slice(0, 4)}.${digits.slice(4)}.`
+ }
+ }
+
+ return formatted
+}
diff --git a/src/utils/formatDate.js b/src/utils/formatDate.ts
similarity index 100%
rename from src/utils/formatDate.js
rename to src/utils/formatDate.ts
diff --git a/src/utils/formatRecruitData.js b/src/utils/formatRecruitData.ts
similarity index 95%
rename from src/utils/formatRecruitData.js
rename to src/utils/formatRecruitData.ts
index 3cfb149..aad2429 100644
--- a/src/utils/formatRecruitData.js
+++ b/src/utils/formatRecruitData.ts
@@ -13,7 +13,6 @@ export const formatRecruitData = (mainRecruitData) => {
gender: mainRecruitDataObject[4]?.gender || "",
birth: mainRecruitDataObject[4]?.birth || "",
major: mainRecruitDataObject[5]?.major || "",
- doubleMajor: mainRecruitDataObject[5]?.doubleMajor || "",
isPayed: mainRecruitDataObject[11]?.isPayed || false
},
answers: {
@@ -27,4 +26,4 @@ export const formatRecruitData = (mainRecruitData) => {
gdgFeedback: mainRecruitDataObject[10]?.gdgFeedback || ""
}
};
-};
\ No newline at end of file
+};
diff --git a/src/utils/localStorageManager.js b/src/utils/localStorageManager.ts
similarity index 63%
rename from src/utils/localStorageManager.js
rename to src/utils/localStorageManager.ts
index aa9226e..225b186 100644
--- a/src/utils/localStorageManager.js
+++ b/src/utils/localStorageManager.ts
@@ -2,10 +2,10 @@
/**
* 데이터를 localStorage에 저장
- * @param {string} storageName - localStorage에 저장할 데이터의 이름
- * @param {any} data - 저장할 데이터
+ * @param storageName - localStorage에 저장할 데이터의 이름
+ * @param data - 저장할 데이터
*/
-const saveToStorage = (storageName, data) => {
+const saveToStorage = (storageName: string, data: any): void => {
try {
const jsonData = JSON.stringify(data);
localStorage.setItem(storageName, jsonData);
@@ -17,15 +17,15 @@ const saveToStorage = (storageName, data) => {
/**
* localStorage에서 데이터 불러오기
- * @param {string} storageName - localStorage에서 불러올 데이터의 이름
- * @param {function} setData - 불러온 데이터를 설정할 함수
+ * @param storageName - localStorage에서 불러올 데이터의 이름
+ * @param setData - 불러온 데이터를 설정할 함수
*/
-const loadFromStorage = (storageName, setData) => {
+const loadFromStorage = (storageName: string, setData: (data: T) => void): void => {
try {
const raw = localStorage.getItem(storageName);
if (!raw) return;
- const parsed = JSON.parse(raw);
+ const parsed = JSON.parse(raw) as T;
setData(parsed);
} catch (err) {
console.error('로드 중 오류 발생');
@@ -35,9 +35,9 @@ const loadFromStorage = (storageName, setData) => {
/**
* localStorage에서 특정 항목 삭제
- * @param {string} storageName - 삭제할 데이터의 이름
+ * @param storageName - 삭제할 데이터의 이름
*/
-const removeFromStorage = (storageName) => {
+const removeFromStorage = (storageName: string): void => {
try {
localStorage.removeItem(storageName);
} catch (err) {
@@ -48,9 +48,8 @@ const removeFromStorage = (storageName) => {
/**
* localStorage 전체 초기화
- * @returns {void}
*/
-const clearStorage = () => {
+const clearStorage = (): void => {
try {
localStorage.clear();
} catch (err) {
diff --git a/src/utils/phoneNumber.ts b/src/utils/phoneNumber.ts
new file mode 100644
index 0000000..437fa24
--- /dev/null
+++ b/src/utils/phoneNumber.ts
@@ -0,0 +1,29 @@
+export const PHONE_NUMBER_PATTERN = /^010-\d{4}-\d{4}$/
+
+export const toPhoneDigits = (value: string): string => value.replace(/\D/g, '')
+
+export const formatPhoneNumberInput = (value: string): string => {
+ const sanitized = value.replace(/[^0-9-]/g, '')
+ const digits = sanitized.replace(/-/g, '').slice(0, 11)
+
+ let formatted = digits
+ if (digits.length > 3) {
+ formatted = `${digits.slice(0, 3)}-${digits.slice(3, Math.min(digits.length, 7))}`
+ }
+ if (digits.length > 7) {
+ formatted = `${digits.slice(0, 3)}-${digits.slice(3, 7)}-${digits.slice(7)}`
+ }
+
+ // Allow manual "-" while typing at valid boundaries.
+ if (sanitized.endsWith('-')) {
+ if (digits.length === 3) return `${digits}-`
+ if (digits.length >= 4 && digits.length <= 7) {
+ return `${digits.slice(0, 3)}-${digits.slice(3)}-`
+ }
+ }
+
+ return formatted
+}
+
+export const isPhoneNumberFormatValid = (value: string): boolean =>
+ PHONE_NUMBER_PATTERN.test(value.trim())
diff --git a/tailwind.config.js b/tailwind.config.js
index 844eb94..f494732 100644
--- a/tailwind.config.js
+++ b/tailwind.config.js
@@ -1,48 +1,38 @@
-const { heroui } = require('@heroui/theme');
-// tailwind.config.js
-const { nextui } = require('@nextui-org/react');
+const { nextui } = require("@nextui-org/react");
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
- './src/pages/**/*.{js,ts,jsx,tsx,mdx}',
- './src/components/**/*.{js,ts,jsx,tsx,mdx}',
- './src/app/**/*.{js,ts,jsx,tsx,mdx}',
- './node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}',
- './node_modules/@heroui/theme/dist/components/scroll-shadow.js',
+ "./src/**/*.{js,ts,jsx,tsx,mdx}",
+ "./node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}",
+ "./node_modules/@heroui/theme/dist/components/scroll-shadow.js"
],
theme: {
extend: {
- fontFamily: {
- 'pretendard': 'var(--font-pretendard)',
- 'ocra': 'var(--font-ocra)',
- },
colors: {
- background: 'var(--background)',
- foreground: 'var(--foreground)',
- cred: "#EA4335", // Google Red
- cblue: "#4285F4", // Google Blue
- cyellow: "#FBBC05", // Google Yellow
- cgreen: "#34A853", // Google Green
- cblack: "#1A1A1A",
- cwhite: "#FAFAFA",
- },
- screens: {
- mobile: { max: '768px' }, // 768px 이하일 때 적용
- pc: { min: '768px' },
- 'tablet': { 'min': '1000px', 'max': '1400px' },
- 'desktop': { 'min': '1400px' },
- },
- keyframes: {
- floatY: {
- '0%, 100%': { transform: 'translateX(-50%) translateY(0)' },
- '50%': { transform: 'translateX(-50%) translateY(-10px)' },
+ black: "#1E1E1E",
+ white: "#F0F0F0",
+ red: {
+ DEFAULT: "#EA4335",
+ 400: "#4F1C17",
+ },
+ blue: {
+ DEFAULT: "#4285F4",
+ 400: "#1E3252",
+ },
+ green: {
+ DEFAULT: "#34A853",
+ 400: "#143D1F",
+ },
+ yellow: {
+ DEFAULT: "#F9AB00",
+ 400: "#493812",
},
- },
- animation: {
- floatY: 'floatY 1.6s ease-in-out infinite',
},
},
},
- plugins: [nextui(), heroui()],
+ darkMode: "class",
+ plugins: [nextui()],
};
+
+
diff --git a/ui-design-system-progress.md b/ui-design-system-progress.md
new file mode 100644
index 0000000..6c90724
--- /dev/null
+++ b/ui-design-system-progress.md
@@ -0,0 +1,50 @@
+# UI Design-System Prompt
+
+## 요청 배경
+Figma 디자인을 코드로 통합해 공용 UI 컴포넌트를 구축하고자 함. 요구 범위는 다음과 같음.
+
+1. **버튼/토글/태그/체크박스/라디오**: PC/M 사이즈, 상태(기본/활성/프레스/비활성), 양쪽 라운드 형태, 빨간 강조 컬러.
+2. **입력(한 줄/장문/검색)**: 폭 토큰(Full, 2/3, Medium, Small, Mini, Quarter), 상태(기본/활성/에러/비활성), 라벨/헬퍼/에러, 장문 박스, 검색 아이콘 포함.
+3. 컴포넌트는 디자인 시스템 위치(`src/components/ui/design-system`)에서 재사용 가능해야 하며 Showcase 컴포넌트로 결과를 확인할 수 있어야 함.
+
+## 현재까지 완료된 작업
+- `src/utils/cn.ts`: className 병합 헬퍼 작성.
+- 디자인 시스템 기본 컴포넌트:
+ - `GdgButton`, `GdgSegmentedButton`, `GdgTag`, `GdgColorTag`, `GdgCheckbox`, `GdgRadio`
+- 입력 계열 컴포넌트:
+ - `GdgInputField` (PC/M, width token, error/disabled, helperText, adornment)
+ - `GdgTextarea` (PC/M, helper/error, auto-resize & 포커스 강조 통일)
+ - `GdgSearchField` (PC/M, full/quarter width, search icon)
+ - `GdgMajorDropdown` (여유 필터 기반 일반 검색, 학과 그룹화, Showcase 예제 포함)
+- 업로드 계열 컴포넌트:
+ - `GdgFileCard` (파일 정보, 삭제/다운로드 액션, PC/M 폭 대응)
+ - `GdgUploadButton` (파일 선택 CTA, PC/M)
+- 드롭다운 컴포넌트:
+ - `GdgDropdown` (PC/M, small/mini/full, NextUI Autocomplete + 기존 스타일 조합, 뱃지/메타 슬롯, Showcase 예제 포함)
+- `src/components/ui/design-system/index.ts`에서 전체 export 정리.
+- `DesignSystemShowcase`에 위 컴포넌트를 실시간으로 확인할 수 있는 섹션 추가(버튼/세그먼트/태그/체크박스/라디오/입력/장문/검색/파일 첨부/드롭다운/학과 드롭다운).
+- `src/app/design-system/page.tsx`: 디자인 시스템 전용 임시 페이지 추가(작업 중 프리뷰/QA용, PR 시 삭제 예정).
+- `npm run lint` 실행 (기존 경고만 존재).
+
+## 남은 TODO
+- 기존 화면에서 새 디자인 시스템 컴포넌트를 실제로 교체.
+- 옵셔널 상태(placeholder, icon set 등) 추후 Figma 업데이트 시 반영.
+- 필요 시 Storybook/Docs 추가.
+
+## 기술 메모
+- **Tailwind CSS v4** 환경을 기본으로 한다. 기존 v3 이전 문법(예: `@apply` 기반 theme 선언, `text-base/6` 표현 등)은 그대로 붙여 넣지 말고 v4 기준으로 재작성한다.
+- v4 `@theme`에서 정의한 토큰은 곧바로 클래스 이름으로 노출되므로, `bg-red`, `text-gray-400`, `border-yellow-400`처럼 Tailwind 유틸을 사용하면 자동으로 토큰을 참조한다.
+
+## 정책 메모
+- `src/components/ui/design-system` 내 모든 컴포넌트/타입은 **named export만 사용**한다. (default export 금지)
+- 화면 구현 시 **디자인 시스템에 정의된 컴포넌트를 우선적으로 사용**한다. 신규 UI가 필요하면 먼저 디자인 시스템에 추가하고 공유한다.
+- **모든 페이지는 레이아웃 그리드 토큰**(`.layout-grid--wide`, `--short`, `--mobile` 등)을 명시적으로 적용해 설계한다. Figma 기준 Long/Short/Mobile 중 해당 화면 성격에 맞는 형태를 판단해 선택하고, 그리드 없이 임의 배치하지 않는다.
+- 페이지의 최상단 래퍼에 적절한 `layout-grid--*`만 부여하고, 내부 컴포넌트는 `col-span-*`으로 세부 단폭을 지정해 배치한다. 한 화면에서 2단/3단/4단을 혼합 사용 가능하며, 반응형을 위해서는 `layout-grid--wide-screen`(Long) 또는 `layout-grid--narrow-screen`(Short) 조합 클래스를 사용해 모바일/PC 폭을 동시에 처리한다.
+- 모바일 개발 시에 상하단 여백은 브라우저 영역을 고려한 것이므로 이를 제외하고 구현한다.
+
+## 컬러 토큰 가이드
+- `src/styles/globals.css`에 정의된 `--color-*` 토큰은 Figma 팔레트와 1:1로 대응한다. (Red/Green/Blue/Yellow 본색 + 400, Gray 100~900, Black/White)
+- 퍼블리싱/개발 시 **HEX/RGB/HSL 직접 입력 금지**. Tailwind 유틸(`text-red`, `bg-gray-200`, `border-white`)을 사용하면 자동으로 토큰을 가리키므로 `text-[var(--color-red)]` 같은 수동 참조는 금지한다.
+- 새로운 색상이 필요하면 먼저 Figma 디자인 시스템에 반영한 뒤 토큰을 추가하고 공유한다. 임의 HEX를 생성해 쓰지 않는다.
+- 투명도·그라데이션 등 변형 색상은 `color-mix` 등으로 기존 토큰을 조합해 표현한다. (예: `bg-[linear-gradient(90deg,var(--color-red),var(--color-yellow))]`)
+- PR 전에 `rg '#[0-9A-Fa-f]{6}' src`를 실행해 하드코딩된 색상이 남지 않았는지 확인하고, 발견되면 즉시 토큰으로 교체한다.
diff --git a/yarn.lock b/yarn.lock
index 295ef44..8792708 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -7,22 +7,44 @@
resolved "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz"
integrity sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==
-"@babel/runtime@^7.12.5", "@babel/runtime@^7.20.13":
- version "7.27.1"
- resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz"
- integrity sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==
+"@babel/runtime@^7.20.13":
+ version "7.28.4"
+ resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz"
+ integrity sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==
-"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.7.0":
- version "4.7.0"
- resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz"
- integrity sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==
+"@emnapi/core@^1.4.0", "@emnapi/core@^1.7.1":
+ version "1.4.3"
+ resolved "https://registry.npmjs.org/@emnapi/core/-/core-1.4.3.tgz"
+ integrity sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==
+ dependencies:
+ "@emnapi/wasi-threads" "1.0.2"
+ tslib "^2.4.0"
+
+"@emnapi/runtime@^1.4.0", "@emnapi/runtime@^1.7.0", "@emnapi/runtime@^1.7.1":
+ version "1.8.1"
+ resolved "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz"
+ integrity sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==
+ dependencies:
+ tslib "^2.4.0"
+
+"@emnapi/wasi-threads@^1.1.0", "@emnapi/wasi-threads@1.0.2":
+ version "1.0.2"
+ resolved "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.0.2.tgz"
+ integrity sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA==
+ dependencies:
+ tslib "^2.4.0"
+
+"@eslint-community/eslint-utils@^4.2.0", "@eslint-community/eslint-utils@^4.9.1":
+ version "4.9.1"
+ resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz"
+ integrity sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==
dependencies:
eslint-visitor-keys "^3.4.3"
-"@eslint-community/regexpp@^4.10.0", "@eslint-community/regexpp@^4.6.1":
- version "4.12.1"
- resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz"
- integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==
+"@eslint-community/regexpp@^4.12.2", "@eslint-community/regexpp@^4.6.1":
+ version "4.12.2"
+ resolved "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz"
+ integrity sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==
"@eslint/eslintrc@^2.1.4":
version "2.1.4"
@@ -103,75 +125,70 @@
resolved "https://registry.npmjs.org/@heroicons/react/-/react-2.2.0.tgz"
integrity sha512-LMcepvRaS9LYHJGsF0zzmgKCUim/X3N/DQKc4jepAXJ7l8QxJ1PmxJzqplF2Z3FE4PqBAIGyJAQ/w4B5dsqbtQ==
-"@heroui/react-rsc-utils@2.1.7":
- version "2.1.7"
- resolved "https://registry.npmjs.org/@heroui/react-rsc-utils/-/react-rsc-utils-2.1.7.tgz"
- integrity sha512-NYKKOLs+KHA8v0+PxkkhVXxTD0WNvC4QMlMjUVshzpWhjnOHIrtXjAtqO6XezWmiKNKY76FAjnMZP+Be5+j5uw==
-
-"@heroui/react-utils@2.1.10":
- version "2.1.10"
- resolved "https://registry.npmjs.org/@heroui/react-utils/-/react-utils-2.1.10.tgz"
- integrity sha512-Wj3BSQnNFrDzDnN44vYEwTScMpdbylbZwO8UxIY02AoQCBD5QW7Wf0r2FVlrsrjPjMOVeogwlVvCBYvZz5hHnQ==
- dependencies:
- "@heroui/react-rsc-utils" "2.1.7"
- "@heroui/shared-utils" "2.1.9"
-
-"@heroui/scroll-shadow@^2.3.6":
- version "2.3.13"
- resolved "https://registry.npmjs.org/@heroui/scroll-shadow/-/scroll-shadow-2.3.13.tgz"
- integrity sha512-RfYfVewf6UR4vr4sIPI2NaNoyK5lLgJwdWNGufE1Km7INelXf3BVdVKLW/Qlq/cES+B4TV3gq5Nto8aen3R1Sg==
- dependencies:
- "@heroui/react-utils" "2.1.10"
- "@heroui/shared-utils" "2.1.9"
- "@heroui/use-data-scroll-overflow" "2.2.10"
-
-"@heroui/shared-utils@2.1.9":
+"@heroui/react-rsc-utils@2.1.9":
version "2.1.9"
- resolved "https://registry.npmjs.org/@heroui/shared-utils/-/shared-utils-2.1.9.tgz"
- integrity sha512-mM/Ep914cYMbw3T/b6+6loYhuNfzDaph76mzw/oIS05gw1Dhp9luCziSiIhqDGgzYck2d74oWTZlahyCsxf47w==
-
-"@heroui/system-rsc@2.3.13":
- version "2.3.13"
- resolved "https://registry.npmjs.org/@heroui/system-rsc/-/system-rsc-2.3.13.tgz"
- integrity sha512-zLBrDKCoM4o039t3JdfYZAOlHmn4RzI6gxU+Tw8XJIfvUzpGSvR2seY2XJBbKOonmTpILlnw16ZvHF+KG+nN0w==
- dependencies:
- "@react-types/shared" "3.29.0"
- clsx "^1.2.1"
-
-"@heroui/system@^2.4.7", "@heroui/system@>=2.4.7":
- version "2.4.15"
- resolved "https://registry.npmjs.org/@heroui/system/-/system-2.4.15.tgz"
- integrity sha512-+QUHscs2RTk5yOFEQXNlQa478P7PTD02ZGP/RTNCviR4E9ZTUifdjfsKA7D4L79S7L8Mkvbz5E2Ruz2ZF0R/IA==
- dependencies:
- "@heroui/react-utils" "2.1.10"
- "@heroui/system-rsc" "2.3.13"
- "@internationalized/date" "3.8.0"
- "@react-aria/i18n" "3.12.8"
- "@react-aria/overlays" "3.27.0"
- "@react-aria/utils" "3.28.2"
- "@react-stately/utils" "3.10.6"
- "@react-types/datepicker" "3.12.0"
-
-"@heroui/theme@^2.4.6", "@heroui/theme@>=2.4.6":
- version "2.4.15"
- resolved "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.15.tgz"
- integrity sha512-cP1N9Rqj5wzsKLpEzNdJQRjX2g9AuCZbRNaIuIGnztqmmGtP3Yykt1RzeQ4ukCdSDjk/PmV8XneTu8OC8Cs8HA==
- dependencies:
- "@heroui/shared-utils" "2.1.9"
- clsx "^1.2.1"
+ resolved "https://registry.npmjs.org/@heroui/react-rsc-utils/-/react-rsc-utils-2.1.9.tgz"
+ integrity sha512-e77OEjNCmQxE9/pnLDDb93qWkX58/CcgIqdNAczT/zUP+a48NxGq2A2WRimvc1uviwaNL2StriE2DmyZPyYW7Q==
+
+"@heroui/react-utils@2.1.14":
+ version "2.1.14"
+ resolved "https://registry.npmjs.org/@heroui/react-utils/-/react-utils-2.1.14.tgz"
+ integrity sha512-hhKklYKy9sRH52C9A8P0jWQ79W4MkIvOnKBIuxEMHhigjfracy0o0lMnAUdEsJni4oZKVJYqNGdQl+UVgcmeDA==
+ dependencies:
+ "@heroui/react-rsc-utils" "2.1.9"
+ "@heroui/shared-utils" "2.1.12"
+
+"@heroui/scroll-shadow@^2.3.19":
+ version "2.3.19"
+ resolved "https://registry.npmjs.org/@heroui/scroll-shadow/-/scroll-shadow-2.3.19.tgz"
+ integrity sha512-y5mdBlhiITVrFnQTDqEphYj7p5pHqoFSFtVuRRvl9wUec2lMxEpD85uMGsfL8OgQTKIAqGh2s6M360+VJm7ajQ==
+ dependencies:
+ "@heroui/react-utils" "2.1.14"
+ "@heroui/shared-utils" "2.1.12"
+ "@heroui/use-data-scroll-overflow" "2.2.13"
+
+"@heroui/shared-utils@2.1.12":
+ version "2.1.12"
+ resolved "https://registry.npmjs.org/@heroui/shared-utils/-/shared-utils-2.1.12.tgz"
+ integrity sha512-0iCnxVAkIPtrHQo26Qa5g0UTqMTpugTbClNOrEPsrQuyRAq7Syux998cPwGlneTfB5E5xcU3LiEdA9GUyeK2cQ==
+
+"@heroui/system-rsc@2.3.21":
+ version "2.3.21"
+ resolved "https://registry.npmjs.org/@heroui/system-rsc/-/system-rsc-2.3.21.tgz"
+ integrity sha512-icB7njbNgkI3dcfZhY5LP7VFspaVgWL1lcg9Q7uJMAaj6gGFqqSSnHkSMwpR9AGLxVRKTHey0TUx8CeZDe8XDw==
+ dependencies:
+ "@react-types/shared" "3.32.1"
+
+"@heroui/system@^2.4.25", "@heroui/system@>=2.4.18":
+ version "2.4.25"
+ resolved "https://registry.npmjs.org/@heroui/system/-/system-2.4.25.tgz"
+ integrity sha512-F6UUoGTQ+Qas5wYkCzLjXE7u74Z9ygO0u0+dkTW7zCaY7ds65CcmvZ/ahKz2ES3Tk6TNks1MJSyaQ9rFLs8AqA==
+ dependencies:
+ "@heroui/react-utils" "2.1.14"
+ "@heroui/system-rsc" "2.3.21"
+ "@react-aria/i18n" "3.12.14"
+ "@react-aria/overlays" "3.31.0"
+ "@react-aria/utils" "3.32.0"
+
+"@heroui/theme@^2.4.25", "@heroui/theme@>=2.4.23":
+ version "2.4.25"
+ resolved "https://registry.npmjs.org/@heroui/theme/-/theme-2.4.25.tgz"
+ integrity sha512-nTptYhO1V9rMoh9SJDnMfaSmFuoXvbem1UuwgHcraRtqy/TIVBPqv26JEGzSoUCL194TDGOJpqrpMuab/PdXcw==
+ dependencies:
+ "@heroui/shared-utils" "2.1.12"
color "^4.2.3"
color2k "^2.0.3"
deepmerge "4.3.1"
flat "^5.0.2"
- tailwind-merge "2.5.4"
- tailwind-variants "0.3.0"
+ tailwind-merge "3.4.0"
+ tailwind-variants "3.2.2"
-"@heroui/use-data-scroll-overflow@2.2.10":
- version "2.2.10"
- resolved "https://registry.npmjs.org/@heroui/use-data-scroll-overflow/-/use-data-scroll-overflow-2.2.10.tgz"
- integrity sha512-Lza9S7ZWhY3PliahSgDRubrpeT7gnySH67GSTrGQMzYggTDMo2I1Pky7ZaHUnHHYB9Y7WHryB26ayWBOgRtZUQ==
+"@heroui/use-data-scroll-overflow@2.2.13":
+ version "2.2.13"
+ resolved "https://registry.npmjs.org/@heroui/use-data-scroll-overflow/-/use-data-scroll-overflow-2.2.13.tgz"
+ integrity sha512-zboLXO1pgYdzMUahDcVt5jf+l1jAQ/D9dFqr7AxWLfn6tn7/EgY0f6xIrgWDgJnM0U3hKxVeY13pAeB4AFTqTw==
dependencies:
- "@heroui/shared-utils" "2.1.9"
+ "@heroui/shared-utils" "2.1.12"
"@humanwhocodes/config-array@^0.13.0":
version "0.13.0"
@@ -192,15 +209,157 @@
resolved "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz"
integrity sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==
-"@img/sharp-win32-x64@0.34.1":
- version "0.34.1"
- resolved "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz"
- integrity sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==
+"@img/colour@^1.0.0":
+ version "1.0.0"
+ resolved "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz"
+ integrity sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==
-"@internationalized/date@^3.6.0", "@internationalized/date@^3.8.0", "@internationalized/date@3.8.0":
- version "3.8.0"
- resolved "https://registry.npmjs.org/@internationalized/date/-/date-3.8.0.tgz"
- integrity sha512-J51AJ0fEL68hE4CwGPa6E0PO6JDaVLd8aln48xFCSy7CZkZc96dGEGmLs2OEEbBxcsVZtfrqkXJwI2/MSG8yKw==
+"@img/sharp-darwin-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz"
+ integrity sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-arm64" "1.2.4"
+
+"@img/sharp-darwin-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz"
+ integrity sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==
+ optionalDependencies:
+ "@img/sharp-libvips-darwin-x64" "1.2.4"
+
+"@img/sharp-libvips-darwin-arm64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz"
+ integrity sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==
+
+"@img/sharp-libvips-darwin-x64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz"
+ integrity sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==
+
+"@img/sharp-libvips-linux-arm@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz"
+ integrity sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==
+
+"@img/sharp-libvips-linux-arm64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz"
+ integrity sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==
+
+"@img/sharp-libvips-linux-ppc64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz"
+ integrity sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==
+
+"@img/sharp-libvips-linux-riscv64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz"
+ integrity sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==
+
+"@img/sharp-libvips-linux-s390x@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz"
+ integrity sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==
+
+"@img/sharp-libvips-linux-x64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz"
+ integrity sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==
+
+"@img/sharp-libvips-linuxmusl-arm64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz"
+ integrity sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==
+
+"@img/sharp-libvips-linuxmusl-x64@1.2.4":
+ version "1.2.4"
+ resolved "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz"
+ integrity sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==
+
+"@img/sharp-linux-arm@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz"
+ integrity sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm" "1.2.4"
+
+"@img/sharp-linux-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz"
+ integrity sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-arm64" "1.2.4"
+
+"@img/sharp-linux-ppc64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz"
+ integrity sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-ppc64" "1.2.4"
+
+"@img/sharp-linux-riscv64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz"
+ integrity sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-riscv64" "1.2.4"
+
+"@img/sharp-linux-s390x@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz"
+ integrity sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-s390x" "1.2.4"
+
+"@img/sharp-linux-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz"
+ integrity sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==
+ optionalDependencies:
+ "@img/sharp-libvips-linux-x64" "1.2.4"
+
+"@img/sharp-linuxmusl-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz"
+ integrity sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-arm64" "1.2.4"
+
+"@img/sharp-linuxmusl-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz"
+ integrity sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==
+ optionalDependencies:
+ "@img/sharp-libvips-linuxmusl-x64" "1.2.4"
+
+"@img/sharp-wasm32@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz"
+ integrity sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==
+ dependencies:
+ "@emnapi/runtime" "^1.7.0"
+
+"@img/sharp-win32-arm64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz"
+ integrity sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==
+
+"@img/sharp-win32-ia32@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz"
+ integrity sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==
+
+"@img/sharp-win32-x64@0.34.5":
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz"
+ integrity sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==
+
+"@internationalized/date@^3.10.1", "@internationalized/date@^3.6.0", "@internationalized/date@^3.8.0":
+ version "3.10.1"
+ resolved "https://registry.npmjs.org/@internationalized/date/-/date-3.10.1.tgz"
+ integrity sha512-oJrXtQiAXLvT9clCf1K4kxp3eKsQhIaZqxEyowkBcsvZDdZkbWrVmnGknxs5flTD0VGsxrxKgBCZty1EzoiMzA==
dependencies:
"@swc/helpers" "^0.5.0"
@@ -211,25 +370,25 @@
dependencies:
"@swc/helpers" "^0.5.0"
-"@internationalized/message@^3.1.6", "@internationalized/message@^3.1.7":
- version "3.1.7"
- resolved "https://registry.npmjs.org/@internationalized/message/-/message-3.1.7.tgz"
- integrity sha512-gLQlhEW4iO7DEFPf/U7IrIdA3UyLGS0opeqouaFwlMObLUzwexRjbygONHDVbC9G9oFLXsLyGKYkJwqXw/QADg==
+"@internationalized/message@^3.1.6", "@internationalized/message@^3.1.8":
+ version "3.1.8"
+ resolved "https://registry.npmjs.org/@internationalized/message/-/message-3.1.8.tgz"
+ integrity sha512-Rwk3j/TlYZhn3HQ6PyXUV0XP9Uv42jqZGNegt0BXlxjE6G3+LwHjbQZAGHhCnCPdaA6Tvd3ma/7QzLlLkJxAWA==
dependencies:
"@swc/helpers" "^0.5.0"
intl-messageformat "^10.1.0"
-"@internationalized/number@^3.6.0", "@internationalized/number@^3.6.1":
- version "3.6.1"
- resolved "https://registry.npmjs.org/@internationalized/number/-/number-3.6.1.tgz"
- integrity sha512-UVsb4bCwbL944E0SX50CHFtWEeZ2uB5VozZ5yDXJdq6iPZsZO5p+bjVMZh2GxHf4Bs/7xtDCcPwEa2NU9DaG/g==
+"@internationalized/number@^3.6.0", "@internationalized/number@^3.6.5":
+ version "3.6.5"
+ resolved "https://registry.npmjs.org/@internationalized/number/-/number-3.6.5.tgz"
+ integrity sha512-6hY4Kl4HPBvtfS62asS/R22JzNNy8vi/Ssev7x6EobfCp+9QIB2hKvI2EtbdJ0VSQacxVNtqhE/NmF/NZ0gm6g==
dependencies:
"@swc/helpers" "^0.5.0"
-"@internationalized/string@^3.2.5", "@internationalized/string@^3.2.6":
- version "3.2.6"
- resolved "https://registry.npmjs.org/@internationalized/string/-/string-3.2.6.tgz"
- integrity sha512-LR2lnM4urJta5/wYJVV7m8qk5DrMZmLRTuFhbQO5b9/sKLHgty6unQy1Li4+Su2DWydmB4aZdS5uxBRXIq2aAw==
+"@internationalized/string@^3.2.5", "@internationalized/string@^3.2.7":
+ version "3.2.7"
+ resolved "https://registry.npmjs.org/@internationalized/string/-/string-3.2.7.tgz"
+ integrity sha512-D4OHBjrinH+PFZPvfCXvG28n2LSykWcJ7GIioQL+ok0LON15SdfoUssoHzzOUmVZLbRoREsQXVzA6r8JKsbP6A==
dependencies:
"@swc/helpers" "^0.5.0"
@@ -245,13 +404,20 @@
wrap-ansi "^8.1.0"
wrap-ansi-cjs "npm:wrap-ansi@^7.0.0"
-"@jridgewell/gen-mapping@^0.3.2":
- version "0.3.8"
- resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz"
- integrity sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==
+"@jridgewell/gen-mapping@^0.3.5":
+ version "0.3.13"
+ resolved "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz"
+ integrity sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==
+ dependencies:
+ "@jridgewell/sourcemap-codec" "^1.5.0"
+ "@jridgewell/trace-mapping" "^0.3.24"
+
+"@jridgewell/remapping@^2.3.4":
+ version "2.3.5"
+ resolved "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz"
+ integrity sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==
dependencies:
- "@jridgewell/set-array" "^1.2.1"
- "@jridgewell/sourcemap-codec" "^1.4.10"
+ "@jridgewell/gen-mapping" "^0.3.5"
"@jridgewell/trace-mapping" "^0.3.24"
"@jridgewell/resolve-uri@^3.1.0":
@@ -259,40 +425,79 @@
resolved "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz"
integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==
-"@jridgewell/set-array@^1.2.1":
- version "1.2.1"
- resolved "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz"
- integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==
-
-"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14":
- version "1.5.0"
- resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz"
- integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==
+"@jridgewell/sourcemap-codec@^1.4.14", "@jridgewell/sourcemap-codec@^1.5.0", "@jridgewell/sourcemap-codec@^1.5.5":
+ version "1.5.5"
+ resolved "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz"
+ integrity sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==
"@jridgewell/trace-mapping@^0.3.24":
- version "0.3.25"
- resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz"
- integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==
+ version "0.3.31"
+ resolved "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz"
+ integrity sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==
dependencies:
"@jridgewell/resolve-uri" "^3.1.0"
"@jridgewell/sourcemap-codec" "^1.4.14"
-"@next/env@15.3.2":
- version "15.3.2"
- resolved "https://registry.npmjs.org/@next/env/-/env-15.3.2.tgz"
- integrity sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==
+"@napi-rs/wasm-runtime@^0.2.9", "@napi-rs/wasm-runtime@^1.1.0":
+ version "0.2.9"
+ resolved "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-0.2.9.tgz"
+ integrity sha512-OKRBiajrrxB9ATokgEQoG87Z25c67pCpYcCwmXYX8PBftC9pBfN18gnm/fh1wurSLEKIAt+QRFLFCQISrb66Jg==
+ dependencies:
+ "@emnapi/core" "^1.4.0"
+ "@emnapi/runtime" "^1.4.0"
+ "@tybys/wasm-util" "^0.9.0"
+
+"@next/env@15.5.9":
+ version "15.5.9"
+ resolved "https://registry.npmjs.org/@next/env/-/env-15.5.9.tgz"
+ integrity sha512-4GlTZ+EJM7WaW2HEZcyU317tIQDjkQIyENDLxYJfSWlfqguN+dHkZgyQTV/7ykvobU7yEH5gKvreNrH4B6QgIg==
-"@next/eslint-plugin-next@15.0.2":
- version "15.0.2"
- resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.0.2.tgz"
- integrity sha512-R9Jc7T6Ge0txjmqpPwqD8vx6onQjynO9JT73ArCYiYPvSrwYXepH/UY/WdKDY8JPWJl72sAE4iGMHPeQ5xdEWg==
+"@next/eslint-plugin-next@15.5.9":
+ version "15.5.9"
+ resolved "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.5.9.tgz"
+ integrity sha512-kUzXx0iFiXw27cQAViE1yKWnz/nF8JzRmwgMRTMh8qMY90crNsdXJRh2e+R0vBpFR3kk1yvAR7wev7+fCCb79Q==
dependencies:
fast-glob "3.3.1"
-"@next/swc-win32-x64-msvc@15.3.2":
- version "15.3.2"
- resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.2.tgz"
- integrity sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA==
+"@next/swc-darwin-arm64@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.5.7.tgz"
+ integrity sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==
+
+"@next/swc-darwin-x64@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.5.7.tgz"
+ integrity sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==
+
+"@next/swc-linux-arm64-gnu@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.5.7.tgz"
+ integrity sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==
+
+"@next/swc-linux-arm64-musl@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.5.7.tgz"
+ integrity sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==
+
+"@next/swc-linux-x64-gnu@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.5.7.tgz"
+ integrity sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==
+
+"@next/swc-linux-x64-musl@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.5.7.tgz"
+ integrity sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==
+
+"@next/swc-win32-arm64-msvc@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.5.7.tgz"
+ integrity sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==
+
+"@next/swc-win32-x64-msvc@15.5.7":
+ version "15.5.7"
+ resolved "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.5.7.tgz"
+ integrity sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==
"@nextui-org/accordion@2.2.7":
version "2.2.7"
@@ -1438,14 +1643,14 @@
"@swc/helpers" "^0.5.0"
clsx "^2.0.0"
-"@react-aria/focus@^3.20.2":
- version "3.20.2"
- resolved "https://registry.npmjs.org/@react-aria/focus/-/focus-3.20.2.tgz"
- integrity sha512-Q3rouk/rzoF/3TuH6FzoAIKrl+kzZi9LHmr8S5EqLAOyP9TXIKG34x2j42dZsAhrw7TbF9gA8tBKwnCNH4ZV+Q==
+"@react-aria/focus@^3.21.3":
+ version "3.21.3"
+ resolved "https://registry.npmjs.org/@react-aria/focus/-/focus-3.21.3.tgz"
+ integrity sha512-FsquWvjSCwC2/sBk4b+OqJyONETUIXQ2vM0YdPAuC+QFQh2DT6TIBo6dOZVSezlhudDla69xFBd6JvCFq1AbUw==
dependencies:
- "@react-aria/interactions" "^3.25.0"
- "@react-aria/utils" "^3.28.2"
- "@react-types/shared" "^3.29.0"
+ "@react-aria/interactions" "^3.26.0"
+ "@react-aria/utils" "^3.32.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
clsx "^2.0.0"
@@ -1461,36 +1666,36 @@
"@swc/helpers" "^0.5.0"
"@react-aria/grid@^3.11.0":
- version "3.13.0"
- resolved "https://registry.npmjs.org/@react-aria/grid/-/grid-3.13.0.tgz"
- integrity sha512-RcuJYA4fyJ83MH3SunU+P5BGkx3LJdQ6kxwqwWGIuI9eUKc7uVbqvN9WN3fI+L0QfxqBFmh7ffRxIdQn7puuzw==
- dependencies:
- "@react-aria/focus" "^3.20.2"
- "@react-aria/i18n" "^3.12.8"
- "@react-aria/interactions" "^3.25.0"
- "@react-aria/live-announcer" "^3.4.2"
- "@react-aria/selection" "^3.24.0"
- "@react-aria/utils" "^3.28.2"
- "@react-stately/collections" "^3.12.3"
- "@react-stately/grid" "^3.11.1"
- "@react-stately/selection" "^3.20.1"
- "@react-types/checkbox" "^3.9.3"
- "@react-types/grid" "^3.3.1"
- "@react-types/shared" "^3.29.0"
+ version "3.14.6"
+ resolved "https://registry.npmjs.org/@react-aria/grid/-/grid-3.14.6.tgz"
+ integrity sha512-xagBKHNPu4Ovt/I5He7T/oIEq82MDMSrRi5Sw3oxSCwwtZpv+7eyKRSrFz9vrNUzNgWCcx5VHLE660bLdeVNDQ==
+ dependencies:
+ "@react-aria/focus" "^3.21.3"
+ "@react-aria/i18n" "^3.12.14"
+ "@react-aria/interactions" "^3.26.0"
+ "@react-aria/live-announcer" "^3.4.4"
+ "@react-aria/selection" "^3.27.0"
+ "@react-aria/utils" "^3.32.0"
+ "@react-stately/collections" "^3.12.8"
+ "@react-stately/grid" "^3.11.7"
+ "@react-stately/selection" "^3.20.7"
+ "@react-types/checkbox" "^3.10.2"
+ "@react-types/grid" "^3.3.6"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
-"@react-aria/i18n@^3.12.4", "@react-aria/i18n@^3.12.8", "@react-aria/i18n@3.12.8":
- version "3.12.8"
- resolved "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.8.tgz"
- integrity sha512-V/Nau9WuwTwxfFffQL4URyKyY2HhRlu9zmzkF2Hw/j5KmEQemD+9jfaLueG2CJu85lYL06JrZXUdnhZgKnqMkA==
- dependencies:
- "@internationalized/date" "^3.8.0"
- "@internationalized/message" "^3.1.7"
- "@internationalized/number" "^3.6.1"
- "@internationalized/string" "^3.2.6"
- "@react-aria/ssr" "^3.9.8"
- "@react-aria/utils" "^3.28.2"
- "@react-types/shared" "^3.29.0"
+"@react-aria/i18n@^3.12.14", "@react-aria/i18n@^3.12.4", "@react-aria/i18n@3.12.14":
+ version "3.12.14"
+ resolved "https://registry.npmjs.org/@react-aria/i18n/-/i18n-3.12.14.tgz"
+ integrity sha512-zYvs1FlLamFD49uneX3i5mPHrAsB3OjVpSWApTcPw8ydxOaphQDp/Q1aqrbcxlrQCcxZdXWHuvLlbkNR4+8jzw==
+ dependencies:
+ "@internationalized/date" "^3.10.1"
+ "@internationalized/message" "^3.1.8"
+ "@internationalized/number" "^3.6.5"
+ "@internationalized/string" "^3.2.7"
+ "@react-aria/ssr" "^3.9.10"
+ "@react-aria/utils" "^3.32.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-aria/i18n@3.12.4":
@@ -1517,15 +1722,15 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-aria/interactions@^3.25.0":
- version "3.25.0"
- resolved "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.25.0.tgz"
- integrity sha512-GgIsDLlO8rDU/nFn6DfsbP9rfnzhm8QFjZkB9K9+r+MTSCn7bMntiWQgMM+5O6BiA8d7C7x4zuN4bZtc0RBdXQ==
+"@react-aria/interactions@^3.26.0":
+ version "3.26.0"
+ resolved "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.26.0.tgz"
+ integrity sha512-AAEcHiltjfbmP1i9iaVw34Mb7kbkiHpYdqieWufldh4aplWgsF11YQZOfaCJW4QoR2ML4Zzoa9nfFwLXA52R7Q==
dependencies:
- "@react-aria/ssr" "^3.9.8"
- "@react-aria/utils" "^3.28.2"
- "@react-stately/flags" "^3.1.1"
- "@react-types/shared" "^3.29.0"
+ "@react-aria/ssr" "^3.9.10"
+ "@react-aria/utils" "^3.32.0"
+ "@react-stately/flags" "^3.1.2"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-aria/label@^3.7.13", "@react-aria/label@3.7.13":
@@ -1564,10 +1769,10 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-aria/live-announcer@^3.4.1", "@react-aria/live-announcer@^3.4.2":
- version "3.4.2"
- resolved "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.4.2.tgz"
- integrity sha512-6+yNF9ZrZ4YJ60Oxy2gKI4/xy6WUv1iePDCFJkgpNVuOEYi8W8czff8ctXu/RPB25OJx5v2sCw9VirRogTo2zA==
+"@react-aria/live-announcer@^3.4.1", "@react-aria/live-announcer@^3.4.4":
+ version "3.4.4"
+ resolved "https://registry.npmjs.org/@react-aria/live-announcer/-/live-announcer-3.4.4.tgz"
+ integrity sha512-PTTBIjNRnrdJOIRTDGNifY2d//kA7GUAwRFJNOEwSNG4FW+Bq9awqLiflw0JkpyB0VNIwou6lqKPHZVLsGWOXA==
dependencies:
"@swc/helpers" "^0.5.0"
@@ -1591,21 +1796,21 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-aria/overlays@^3.24.0", "@react-aria/overlays@3.27.0":
- version "3.27.0"
- resolved "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.27.0.tgz"
- integrity sha512-2vZVgL7FrloN5Rh8sAhadGADJbuWg69DdSJB3fd2/h5VvcEhnIfNPu9Ma5XmdkApDoTboIEsKZ4QLYwRl98w6w==
- dependencies:
- "@react-aria/focus" "^3.20.2"
- "@react-aria/i18n" "^3.12.8"
- "@react-aria/interactions" "^3.25.0"
- "@react-aria/ssr" "^3.9.8"
- "@react-aria/utils" "^3.28.2"
- "@react-aria/visually-hidden" "^3.8.22"
- "@react-stately/overlays" "^3.6.15"
- "@react-types/button" "^3.12.0"
- "@react-types/overlays" "^3.8.14"
- "@react-types/shared" "^3.29.0"
+"@react-aria/overlays@^3.24.0", "@react-aria/overlays@3.31.0":
+ version "3.31.0"
+ resolved "https://registry.npmjs.org/@react-aria/overlays/-/overlays-3.31.0.tgz"
+ integrity sha512-Vq41X1s8XheGIhGbbuqRJslJEX08qmMVX//dwuBaFX9T18mMR04tumKOMxp8Lz+vqwdGLvjNUYDMcgolL+AMjw==
+ dependencies:
+ "@react-aria/focus" "^3.21.3"
+ "@react-aria/i18n" "^3.12.14"
+ "@react-aria/interactions" "^3.26.0"
+ "@react-aria/ssr" "^3.9.10"
+ "@react-aria/utils" "^3.32.0"
+ "@react-aria/visually-hidden" "^3.8.29"
+ "@react-stately/overlays" "^3.6.21"
+ "@react-types/button" "^3.14.1"
+ "@react-types/overlays" "^3.9.2"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-aria/overlays@3.24.0":
@@ -1666,17 +1871,17 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-aria/selection@^3.24.0":
- version "3.24.0"
- resolved "https://registry.npmjs.org/@react-aria/selection/-/selection-3.24.0.tgz"
- integrity sha512-RfGXVc04zz41NVIW89/a3quURZ4LT/GJLkiajQK2VjhisidPdrAWkcfjjWJj0n+tm5gPWbi9Rs5R/Rc8mrvq8Q==
- dependencies:
- "@react-aria/focus" "^3.20.2"
- "@react-aria/i18n" "^3.12.8"
- "@react-aria/interactions" "^3.25.0"
- "@react-aria/utils" "^3.28.2"
- "@react-stately/selection" "^3.20.1"
- "@react-types/shared" "^3.29.0"
+"@react-aria/selection@^3.27.0":
+ version "3.27.0"
+ resolved "https://registry.npmjs.org/@react-aria/selection/-/selection-3.27.0.tgz"
+ integrity sha512-4zgreuCu4QM4t2U7aF3mbMvIKCEkTEo6h6nGJvbyZALZ/eFtLTvUiV8/5CGDJRLGvgMvi3XxUeF9PZbpk5nMJg==
+ dependencies:
+ "@react-aria/focus" "^3.21.3"
+ "@react-aria/i18n" "^3.12.14"
+ "@react-aria/interactions" "^3.26.0"
+ "@react-aria/utils" "^3.32.0"
+ "@react-stately/selection" "^3.20.7"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-aria/slider@3.7.14":
@@ -1695,15 +1900,22 @@
"@swc/helpers" "^0.5.0"
"@react-aria/spinbutton@^3.6.10":
- version "3.6.14"
- resolved "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.6.14.tgz"
- integrity sha512-oSKe9p0Q/7W39eXRnLxlwJG5dQo4ffosRT3u2AtOcFkk2Zzj+tSQFzHQ4202nrWdzRnQ2KLTgUUNnUvXf0BJcg==
- dependencies:
- "@react-aria/i18n" "^3.12.8"
- "@react-aria/live-announcer" "^3.4.2"
- "@react-aria/utils" "^3.28.2"
- "@react-types/button" "^3.12.0"
- "@react-types/shared" "^3.29.0"
+ version "3.7.0"
+ resolved "https://registry.npmjs.org/@react-aria/spinbutton/-/spinbutton-3.7.0.tgz"
+ integrity sha512-FOyH94BZp+jNhUJuZqXSubQZDNQEJyW/J19/gwCxQvQvxAP79dhDFshh1UtrL4EjbjIflmaOes+sH/XEHUnJVA==
+ dependencies:
+ "@react-aria/i18n" "^3.12.14"
+ "@react-aria/live-announcer" "^3.4.4"
+ "@react-aria/utils" "^3.32.0"
+ "@react-types/button" "^3.14.1"
+ "@react-types/shared" "^3.32.1"
+ "@swc/helpers" "^0.5.0"
+
+"@react-aria/ssr@^3.9.10":
+ version "3.9.10"
+ resolved "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.10.tgz"
+ integrity sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==
+ dependencies:
"@swc/helpers" "^0.5.0"
"@react-aria/ssr@^3.9.7", "@react-aria/ssr@3.9.7":
@@ -1713,13 +1925,6 @@
dependencies:
"@swc/helpers" "^0.5.0"
-"@react-aria/ssr@^3.9.8":
- version "3.9.8"
- resolved "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.8.tgz"
- integrity sha512-lQDE/c9uTfBSDOjaZUJS8xP2jCKVk4zjQeIlCH90xaLhHDgbpCdns3xvFpJJujfj3nI4Ll9K7A+ONUBDCASOuw==
- dependencies:
- "@swc/helpers" "^0.5.0"
-
"@react-aria/switch@3.6.10":
version "3.6.10"
resolved "https://registry.npmjs.org/@react-aria/switch/-/switch-3.6.10.tgz"
@@ -1782,15 +1987,15 @@
"@swc/helpers" "^0.5.0"
"@react-aria/toggle@^3.10.10":
- version "3.11.2"
- resolved "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.11.2.tgz"
- integrity sha512-JOg8yYYCjLDnEpuggPo9GyXFaT/B238d3R8i/xQ6KLelpi3fXdJuZlFD6n9NQp3DJbE8Wj+wM5/VFFAi3cISpw==
- dependencies:
- "@react-aria/interactions" "^3.25.0"
- "@react-aria/utils" "^3.28.2"
- "@react-stately/toggle" "^3.8.3"
- "@react-types/checkbox" "^3.9.3"
- "@react-types/shared" "^3.29.0"
+ version "3.12.3"
+ resolved "https://registry.npmjs.org/@react-aria/toggle/-/toggle-3.12.3.tgz"
+ integrity sha512-mciUbeVP99fRObnH5qLFrkKXX+5VKeV6BhFJlmz1eo3ltR/0xZKnUcycA2CGzmqtB70w09CAhr8NMEnpNH8dwQ==
+ dependencies:
+ "@react-aria/interactions" "^3.26.0"
+ "@react-aria/utils" "^3.32.0"
+ "@react-stately/toggle" "^3.9.3"
+ "@react-types/checkbox" "^3.10.2"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-aria/toolbar@3.0.0-beta.11":
@@ -1817,15 +2022,15 @@
"@react-types/tooltip" "^3.4.13"
"@swc/helpers" "^0.5.0"
-"@react-aria/utils@^3.26.0", "@react-aria/utils@^3.28.2", "@react-aria/utils@3.28.2":
- version "3.28.2"
- resolved "https://registry.npmjs.org/@react-aria/utils/-/utils-3.28.2.tgz"
- integrity sha512-J8CcLbvnQgiBn54eeEvQQbIOfBF3A1QizxMw9P4cl9MkeR03ug7RnjTIdJY/n2p7t59kLeAB3tqiczhcj+Oi5w==
+"@react-aria/utils@^3.26.0", "@react-aria/utils@^3.32.0", "@react-aria/utils@3.32.0":
+ version "3.32.0"
+ resolved "https://registry.npmjs.org/@react-aria/utils/-/utils-3.32.0.tgz"
+ integrity sha512-/7Rud06+HVBIlTwmwmJa2W8xVtgxgzm0+kLbuFooZRzKDON6hhozS1dOMR/YLMxyJOaYOTpImcP4vRR9gL1hEg==
dependencies:
- "@react-aria/ssr" "^3.9.8"
- "@react-stately/flags" "^3.1.1"
- "@react-stately/utils" "^3.10.6"
- "@react-types/shared" "^3.29.0"
+ "@react-aria/ssr" "^3.9.10"
+ "@react-stately/flags" "^3.1.2"
+ "@react-stately/utils" "^3.11.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
clsx "^2.0.0"
@@ -1850,20 +2055,20 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-aria/visually-hidden@^3.8.22":
- version "3.8.22"
- resolved "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.22.tgz"
- integrity sha512-EO3R8YTKZ7HkLl9k1Y2uBKYBgpJagth4/4W7mfpJZE24A3fQnCP8zx1sweXiAm0mirR4J6tNaK7Ia8ssP5TpOw==
+"@react-aria/visually-hidden@^3.8.29":
+ version "3.8.29"
+ resolved "https://registry.npmjs.org/@react-aria/visually-hidden/-/visually-hidden-3.8.29.tgz"
+ integrity sha512-1joCP+MHBLd+YA6Gb08nMFfDBhOF0Kh1gR1SA8zoxEB5RMfQEEkufIB8k0GGwvHGSCK3gFyO8UAVsD0+rRYEyg==
dependencies:
- "@react-aria/interactions" "^3.25.0"
- "@react-aria/utils" "^3.28.2"
- "@react-types/shared" "^3.29.0"
+ "@react-aria/interactions" "^3.26.0"
+ "@react-aria/utils" "^3.32.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
-"@react-google-maps/api@^2.20.7":
- version "2.20.7"
- resolved "https://registry.npmjs.org/@react-google-maps/api/-/api-2.20.7.tgz"
- integrity sha512-ys7uri3V6gjhYZUI43srHzSKDC6/jiKTwHNlwXFTvjeaJE3M3OaYBt9FZKvJs8qnOhL6i6nD1BKJoi1KrnkCkg==
+"@react-google-maps/api@^2.20.8":
+ version "2.20.8"
+ resolved "https://registry.npmjs.org/@react-google-maps/api/-/api-2.20.8.tgz"
+ integrity sha512-wtLYFtCGXK3qbIz1H5to3JxbosPnKsvjDKhqGylXUb859EskhzR7OpuNt0LqdLarXUtZCJTKzPn3BNaekNIahg==
dependencies:
"@googlemaps/js-api-loader" "1.16.8"
"@googlemaps/markerclusterer" "2.5.3"
@@ -1912,12 +2117,12 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-stately/collections@^3.12.3":
- version "3.12.3"
- resolved "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.3.tgz"
- integrity sha512-QfSBME2QWDjUw/RmmUjrYl/j1iCYcYCIDsgZda1OeRtt63R11k0aqmmwrDRwCsA+Sv+D5QgkOp4KK+CokTzoVQ==
+"@react-stately/collections@^3.12.8":
+ version "3.12.8"
+ resolved "https://registry.npmjs.org/@react-stately/collections/-/collections-3.12.8.tgz"
+ integrity sha512-AceJYLLXt1Y2XIcOPi6LEJSs4G/ubeYW3LqOCQbhfIgMaNqKfQMIfagDnPeJX9FVmPFSlgoCBxb1pTJW2vjCAQ==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-stately/combobox@^3.10.1", "@react-stately/combobox@3.10.1":
@@ -1949,10 +2154,10 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-stately/flags@^3.0.5", "@react-stately/flags@^3.1.1":
- version "3.1.1"
- resolved "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.1.tgz"
- integrity sha512-XPR5gi5LfrPdhxZzdIlJDz/B5cBf63l4q6/AzNqVWFKgd0QqY5LvWJftXkklaIUpKSJkIKQb8dphuZXDtkWNqg==
+"@react-stately/flags@^3.0.5", "@react-stately/flags@^3.1.2":
+ version "3.1.2"
+ resolved "https://registry.npmjs.org/@react-stately/flags/-/flags-3.1.2.tgz"
+ integrity sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==
dependencies:
"@swc/helpers" "^0.5.0"
@@ -1964,23 +2169,23 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-stately/form@^3.1.3":
- version "3.1.3"
- resolved "https://registry.npmjs.org/@react-stately/form/-/form-3.1.3.tgz"
- integrity sha512-Jisgm0facSS3sAzHfSgshoCo3LxfO0wmQj98MOBCGXyVL+MSwx2ilb38eXIyBCzHJzJnPRTLaK/E4T49aph47A==
+"@react-stately/form@^3.2.2":
+ version "3.2.2"
+ resolved "https://registry.npmjs.org/@react-stately/form/-/form-3.2.2.tgz"
+ integrity sha512-soAheOd7oaTO6eNs6LXnfn0tTqvOoe3zN9FvtIhhrErKz9XPc5sUmh3QWwR45+zKbitOi1HOjfA/gifKhZcfWw==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
-"@react-stately/grid@^3.10.0", "@react-stately/grid@^3.11.1":
- version "3.11.1"
- resolved "https://registry.npmjs.org/@react-stately/grid/-/grid-3.11.1.tgz"
- integrity sha512-xMk2YsaIKkF8dInRLUFpUXBIqnYt88hehhq2nb65RFgsFFhngE/OkaFudSUzaYPc1KvHpW+oHqvseC+G1iDG2w==
+"@react-stately/grid@^3.10.0", "@react-stately/grid@^3.11.7":
+ version "3.11.7"
+ resolved "https://registry.npmjs.org/@react-stately/grid/-/grid-3.11.7.tgz"
+ integrity sha512-SqzBSxUTFZKLZicfXDK+M0A3gh07AYK1pmU/otcq2cjZ0nSC4CceKijQ2GBZnl+YGcGHI1RgkhpLP6ZioMYctQ==
dependencies:
- "@react-stately/collections" "^3.12.3"
- "@react-stately/selection" "^3.20.1"
- "@react-types/grid" "^3.3.1"
- "@react-types/shared" "^3.29.0"
+ "@react-stately/collections" "^3.12.8"
+ "@react-stately/selection" "^3.20.7"
+ "@react-types/grid" "^3.3.6"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-stately/list@^3.11.1", "@react-stately/list@3.11.1":
@@ -1994,15 +2199,15 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-stately/list@^3.12.1":
- version "3.12.1"
- resolved "https://registry.npmjs.org/@react-stately/list/-/list-3.12.1.tgz"
- integrity sha512-N+YCInNZ2OpY0WUNvJWUTyFHtzE5yBtZ9DI4EHJDvm61+jmZ2s3HszOfa7j+7VOKq78VW3m5laqsQNWvMrLFrQ==
+"@react-stately/list@^3.13.2":
+ version "3.13.2"
+ resolved "https://registry.npmjs.org/@react-stately/list/-/list-3.13.2.tgz"
+ integrity sha512-dGFALuQWNNOkv7W12qSsXLF4mJHLeWeK2hVvdyj4SI8Vxku+BOfaVKuW3sn3mNiixI1dM/7FY2ip4kK+kv27vw==
dependencies:
- "@react-stately/collections" "^3.12.3"
- "@react-stately/selection" "^3.20.1"
- "@react-stately/utils" "^3.10.6"
- "@react-types/shared" "^3.29.0"
+ "@react-stately/collections" "^3.12.8"
+ "@react-stately/selection" "^3.20.7"
+ "@react-stately/utils" "^3.11.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-stately/menu@^3.9.0", "@react-stately/menu@3.9.0":
@@ -2024,13 +2229,13 @@
"@react-types/overlays" "^3.8.11"
"@swc/helpers" "^0.5.0"
-"@react-stately/overlays@^3.6.15":
- version "3.6.15"
- resolved "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.15.tgz"
- integrity sha512-LBaGpXuI+SSd5HSGzyGJA0Gy09V2tl2G/r0lllTYqwt0RDZR6p7IrhdGVXZm6vI0oWEnih7yLC32krkVQrffgQ==
+"@react-stately/overlays@^3.6.21":
+ version "3.6.21"
+ resolved "https://registry.npmjs.org/@react-stately/overlays/-/overlays-3.6.21.tgz"
+ integrity sha512-7f25H1PS2g+SNvuWPEW30pSGqYNHxesCP4w+1RcV/XV1oQI7oP5Ji2WfI0QsJEFc9wP/ZO1pyjHNKpfLI3O88g==
dependencies:
- "@react-stately/utils" "^3.10.6"
- "@react-types/overlays" "^3.8.14"
+ "@react-stately/utils" "^3.11.0"
+ "@react-types/overlays" "^3.9.2"
"@swc/helpers" "^0.5.0"
"@react-stately/radio@^3.10.9", "@react-stately/radio@3.10.9":
@@ -2045,25 +2250,26 @@
"@swc/helpers" "^0.5.0"
"@react-stately/select@^3.6.9":
- version "3.6.12"
- resolved "https://registry.npmjs.org/@react-stately/select/-/select-3.6.12.tgz"
- integrity sha512-5o/NAaENO/Gxs1yui5BHLItxLnDPSQJ5HDKycuD0/gGC17BboAGEY/F9masiQ5qwRPe3JEc0QfvMRq3yZVNXog==
- dependencies:
- "@react-stately/form" "^3.1.3"
- "@react-stately/list" "^3.12.1"
- "@react-stately/overlays" "^3.6.15"
- "@react-types/select" "^3.9.11"
- "@react-types/shared" "^3.29.0"
+ version "3.9.0"
+ resolved "https://registry.npmjs.org/@react-stately/select/-/select-3.9.0.tgz"
+ integrity sha512-eNE33zVYpVdCPKRPGYyViN3LnEq82e1wjBIrs9T7Vo4EBnJeT57pqMZpalTPk7qsA+861t14Qrj7GnUd+YbEXw==
+ dependencies:
+ "@react-stately/form" "^3.2.2"
+ "@react-stately/list" "^3.13.2"
+ "@react-stately/overlays" "^3.6.21"
+ "@react-stately/utils" "^3.11.0"
+ "@react-types/select" "^3.12.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
-"@react-stately/selection@^3.18.0", "@react-stately/selection@^3.20.1":
- version "3.20.1"
- resolved "https://registry.npmjs.org/@react-stately/selection/-/selection-3.20.1.tgz"
- integrity sha512-K9MP6Rfg2yvFoY2Cr+ykA7bP4EBXlGaq5Dqfa1krvcXlEgMbQka5muLHdNXqjzGgcwPmS1dx1NECD15q63NtOw==
+"@react-stately/selection@^3.18.0", "@react-stately/selection@^3.20.7":
+ version "3.20.7"
+ resolved "https://registry.npmjs.org/@react-stately/selection/-/selection-3.20.7.tgz"
+ integrity sha512-NkiRsNCfORBIHNF1bCavh4Vvj+Yd5NffE10iXtaFuhF249NlxLynJZmkcVCqNP9taC2pBIHX00+9tcBgxhG+mA==
dependencies:
- "@react-stately/collections" "^3.12.3"
- "@react-stately/utils" "^3.10.6"
- "@react-types/shared" "^3.29.0"
+ "@react-stately/collections" "^3.12.8"
+ "@react-stately/utils" "^3.11.0"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-stately/slider@^3.6.0", "@react-stately/slider@3.6.0":
@@ -2111,14 +2317,14 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-stately/toggle@^3.8.3":
- version "3.8.3"
- resolved "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.8.3.tgz"
- integrity sha512-4T2V3P1RK4zEFz4vJjUXUXyB0g4Slm6stE6Ry20fzDWjltuW42cD2lmrd7ccTO/CXFmHLECcXQLD4GEbOj0epA==
+"@react-stately/toggle@^3.9.3":
+ version "3.9.3"
+ resolved "https://registry.npmjs.org/@react-stately/toggle/-/toggle-3.9.3.tgz"
+ integrity sha512-G6aA/aTnid/6dQ9dxNEd7/JqzRmVkVYYpOAP+l02hepiuSmFwLu4nE98i4YFBQqFZ5b4l01gMrS90JGL7HrNmw==
dependencies:
- "@react-stately/utils" "^3.10.6"
- "@react-types/checkbox" "^3.9.3"
- "@react-types/shared" "^3.29.0"
+ "@react-stately/utils" "^3.11.0"
+ "@react-types/checkbox" "^3.10.2"
+ "@react-types/shared" "^3.32.1"
"@swc/helpers" "^0.5.0"
"@react-stately/tooltip@^3.5.0", "@react-stately/tooltip@3.5.0":
@@ -2141,10 +2347,10 @@
"@react-types/shared" "^3.26.0"
"@swc/helpers" "^0.5.0"
-"@react-stately/utils@^3.10.5", "@react-stately/utils@^3.10.6", "@react-stately/utils@3.10.6":
- version "3.10.6"
- resolved "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.6.tgz"
- integrity sha512-O76ip4InfTTzAJrg8OaZxKU4vvjMDOpfA/PGNOytiXwBbkct2ZeZwaimJ8Bt9W1bj5VsZ81/o/tW4BacbdDOMA==
+"@react-stately/utils@^3.10.5", "@react-stately/utils@^3.11.0":
+ version "3.11.0"
+ resolved "https://registry.npmjs.org/@react-stately/utils/-/utils-3.11.0.tgz"
+ integrity sha512-8LZpYowJ9eZmmYLpudbo/eclIRnbhWIJZ994ncmlKlouNzKohtM8qTC6B1w1pwUbiwGdUoyzLuQbeaIor5Dvcw==
dependencies:
"@swc/helpers" "^0.5.0"
@@ -2186,12 +2392,12 @@
dependencies:
"@react-types/shared" "^3.26.0"
-"@react-types/button@^3.12.0":
- version "3.12.0"
- resolved "https://registry.npmjs.org/@react-types/button/-/button-3.12.0.tgz"
- integrity sha512-YrASNa+RqGQpzJcxNAahzNuTYVID1OE6HCorrEOXIyGS3EGogHsQmFs9OyThXnGHq6q4rLlA806/jWbP9uZdxA==
+"@react-types/button@^3.14.1":
+ version "3.14.1"
+ resolved "https://registry.npmjs.org/@react-types/button/-/button-3.14.1.tgz"
+ integrity sha512-D8C4IEwKB7zEtiWYVJ3WE/5HDcWlze9mLWQ5hfsBfpePyWCgO3bT/+wjb/7pJvcAocrkXo90QrMm85LcpBtrpg==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@react-types/calendar@^3.5.0", "@react-types/calendar@3.5.0":
version "3.5.0"
@@ -2209,6 +2415,13 @@
"@internationalized/date" "^3.8.0"
"@react-types/shared" "^3.29.0"
+"@react-types/checkbox@^3.10.2":
+ version "3.10.2"
+ resolved "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.10.2.tgz"
+ integrity sha512-ktPkl6ZfIdGS1tIaGSU/2S5Agf2NvXI9qAgtdMDNva0oLyAZ4RLQb6WecPvofw1J7YKXu0VA5Mu7nlX+FM2weQ==
+ dependencies:
+ "@react-types/shared" "^3.32.1"
+
"@react-types/checkbox@^3.9.0", "@react-types/checkbox@3.9.0":
version "3.9.0"
resolved "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.0.tgz"
@@ -2216,13 +2429,6 @@
dependencies:
"@react-types/shared" "^3.26.0"
-"@react-types/checkbox@^3.9.3":
- version "3.9.3"
- resolved "https://registry.npmjs.org/@react-types/checkbox/-/checkbox-3.9.3.tgz"
- integrity sha512-h6wmK7CraKHKE6L13Ut+CtnjRktbMRhkCSorv7eg82M6p4PDhZ7mfDSh13IlGR4sryT8Ka+aOjOU+EvMrKiduA==
- dependencies:
- "@react-types/shared" "^3.29.0"
-
"@react-types/combobox@^3.13.1", "@react-types/combobox@3.13.1":
version "3.13.1"
resolved "https://registry.npmjs.org/@react-types/combobox/-/combobox-3.13.1.tgz"
@@ -2230,7 +2436,7 @@
dependencies:
"@react-types/shared" "^3.26.0"
-"@react-types/datepicker@^3.9.0", "@react-types/datepicker@3.12.0":
+"@react-types/datepicker@^3.9.0":
version "3.12.0"
resolved "https://registry.npmjs.org/@react-types/datepicker/-/datepicker-3.12.0.tgz"
integrity sha512-dw/xflOdQPQ3uEABaBrZRTvjsMRu5/VZjRx9ygc64sX2N7HKIt+foMPXKJ+1jhtki2p4gigNVjcnJndJHoj9SA==
@@ -2272,12 +2478,12 @@
dependencies:
"@react-types/shared" "^3.26.0"
-"@react-types/grid@^3.3.1":
- version "3.3.1"
- resolved "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.1.tgz"
- integrity sha512-bPDckheJiHSIzSeSkLqrO6rXRLWvciFJr9rpCjq/+wBj6HsLh2iMpkB/SqmRHTGpPlJvlu0b7AlxK1FYE0QSKA==
+"@react-types/grid@^3.3.6":
+ version "3.3.6"
+ resolved "https://registry.npmjs.org/@react-types/grid/-/grid-3.3.6.tgz"
+ integrity sha512-vIZJlYTii2n1We9nAugXwM2wpcpsC6JigJFBd6vGhStRdRWRoU4yv1Gc98Usbx0FQ/J7GLVIgeG8+1VMTKBdxw==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@react-types/link@^3.5.9", "@react-types/link@3.5.9":
version "3.5.9"
@@ -2287,11 +2493,11 @@
"@react-types/shared" "^3.26.0"
"@react-types/listbox@^3.5.3":
- version "3.6.0"
- resolved "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.6.0.tgz"
- integrity sha512-+1ugDKTxson/WNOQZO4BfrnQ6cGDt+72mEytXMsSsd4aEC+x3RyUv6NKwdOl4n602cOreo0MHtap1X2BOACVoQ==
+ version "3.7.4"
+ resolved "https://registry.npmjs.org/@react-types/listbox/-/listbox-3.7.4.tgz"
+ integrity sha512-p4YEpTl/VQGrqVE8GIfqTS5LkT5jtjDTbVeZgrkPnX/fiPhsfbTPiZ6g0FNap4+aOGJFGEEZUv2q4vx+rCORww==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@react-types/menu@^3.9.13", "@react-types/menu@3.9.13":
version "3.9.13"
@@ -2315,6 +2521,13 @@
dependencies:
"@react-types/shared" "^3.29.0"
+"@react-types/overlays@^3.9.2":
+ version "3.9.2"
+ resolved "https://registry.npmjs.org/@react-types/overlays/-/overlays-3.9.2.tgz"
+ integrity sha512-Q0cRPcBGzNGmC8dBuHyoPR7N3057KTS5g+vZfQ53k8WwmilXBtemFJPLsogJbspuewQ/QJ3o2HYsp2pne7/iNw==
+ dependencies:
+ "@react-types/shared" "^3.32.1"
+
"@react-types/progress@^3.5.8", "@react-types/progress@3.5.8":
version "3.5.8"
resolved "https://registry.npmjs.org/@react-types/progress/-/progress-3.5.8.tgz"
@@ -2329,12 +2542,12 @@
dependencies:
"@react-types/shared" "^3.26.0"
-"@react-types/select@^3.9.11":
- version "3.9.11"
- resolved "https://registry.npmjs.org/@react-types/select/-/select-3.9.11.tgz"
- integrity sha512-uEpQCgDlrq/5fW05FgNEsqsqpvZVKfHQO9Mp7OTqGtm4UBNAbcQ6hOV7MJwQCS25Lu2luzOYdgqDUN8eAATJVQ==
+"@react-types/select@^3.12.0":
+ version "3.12.0"
+ resolved "https://registry.npmjs.org/@react-types/select/-/select-3.12.0.tgz"
+ integrity sha512-tM3mEbQNotvCJs1gYRFyIeXmXrIBSBLGw7feCIaYSO45IyjCGv8NZwpQWjoKPaWo3GpbHfHMNlWlq3v5QQPIXw==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@react-types/select@3.9.8":
version "3.9.8"
@@ -2343,10 +2556,10 @@
dependencies:
"@react-types/shared" "^3.26.0"
-"@react-types/shared@^3.26.0", "@react-types/shared@^3.29.0", "@react-types/shared@3.29.0":
- version "3.29.0"
- resolved "https://registry.npmjs.org/@react-types/shared/-/shared-3.29.0.tgz"
- integrity sha512-IDQYu/AHgZimObzCFdNl1LpZvQW/xcfLt3v20sorl5qRucDVj4S9os98sVTZ4IRIBjmS+MkjqpR5E70xan7ooA==
+"@react-types/shared@^3.26.0", "@react-types/shared@^3.29.0", "@react-types/shared@^3.32.1", "@react-types/shared@3.32.1":
+ version "3.32.1"
+ resolved "https://registry.npmjs.org/@react-types/shared/-/shared-3.32.1.tgz"
+ integrity sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==
"@react-types/shared@3.26.0":
version "3.26.0"
@@ -2354,18 +2567,18 @@
integrity sha512-6FuPqvhmjjlpEDLTiYx29IJCbCNWPlsyO+ZUmCUXzhUv2ttShOXfw8CmeHWHftT/b2KweAWuzqSlfeXPR76jpw==
"@react-types/slider@^3.7.7":
- version "3.7.10"
- resolved "https://registry.npmjs.org/@react-types/slider/-/slider-3.7.10.tgz"
- integrity sha512-Yb8wbpu2gS7AwvJUuz0IdZBRi6eIBZq32BSss4UHX0StA8dtR1/K4JeTsArxwiA3P0BA6t0gbR6wzxCvVA9fRw==
+ version "3.8.2"
+ resolved "https://registry.npmjs.org/@react-types/slider/-/slider-3.8.2.tgz"
+ integrity sha512-MQYZP76OEOYe7/yA2To+Dl0LNb0cKKnvh5JtvNvDnAvEprn1RuLiay8Oi/rTtXmc2KmBa4VdTcsXsmkbbkeN2Q==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@react-types/switch@^3.5.7":
- version "3.5.10"
- resolved "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.10.tgz"
- integrity sha512-YyNhx4CvuJ0Rvv7yMuQaqQuOIeg+NwLV00NHHJ+K0xEANSLcICLOLPNMOqRIqLSQDz5vDI705UKk8gVcxqPX5g==
+ version "3.5.15"
+ resolved "https://registry.npmjs.org/@react-types/switch/-/switch-3.5.15.tgz"
+ integrity sha512-r/ouGWQmIeHyYSP1e5luET+oiR7N7cLrAlWsrAfYRWHxqXOSNQloQnZJ3PLHrKFT02fsrQhx2rHaK2LfKeyN3A==
dependencies:
- "@react-types/shared" "^3.29.0"
+ "@react-types/shared" "^3.32.1"
"@react-types/table@^3.10.3", "@react-types/table@3.10.3":
version "3.10.3"
@@ -2451,11 +2664,6 @@
resolved "https://registry.npmjs.org/@studio-freight/lenis/-/lenis-1.0.42.tgz"
integrity sha512-HJAGf2DeM+BTvKzHv752z6Z7zy6bA643nZM7W88Ft9tnw2GsJSp6iJ+3cekjyMIWH+cloL2U9X82dKXgdU8kPg==
-"@swc/counter@0.1.3":
- version "0.1.3"
- resolved "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz"
- integrity sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==
-
"@swc/helpers@^0.5.0":
version "0.5.17"
resolved "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.17.tgz"
@@ -2470,6 +2678,115 @@
dependencies:
tslib "^2.8.0"
+"@tailwindcss/node@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.18.tgz"
+ integrity sha512-DoR7U1P7iYhw16qJ49fgXUlry1t4CpXeErJHnQ44JgTSKMaZUdf17cfn5mHchfJ4KRBZRFA/Coo+MUF5+gOaCQ==
+ dependencies:
+ "@jridgewell/remapping" "^2.3.4"
+ enhanced-resolve "^5.18.3"
+ jiti "^2.6.1"
+ lightningcss "1.30.2"
+ magic-string "^0.30.21"
+ source-map-js "^1.2.1"
+ tailwindcss "4.1.18"
+
+"@tailwindcss/oxide-android-arm64@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.18.tgz"
+ integrity sha512-dJHz7+Ugr9U/diKJA0W6N/6/cjI+ZTAoxPf9Iz9BFRF2GzEX8IvXxFIi/dZBloVJX/MZGvRuFA9rqwdiIEZQ0Q==
+
+"@tailwindcss/oxide-darwin-arm64@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.18.tgz"
+ integrity sha512-Gc2q4Qhs660bhjyBSKgq6BYvwDz4G+BuyJ5H1xfhmDR3D8HnHCmT/BSkvSL0vQLy/nkMLY20PQ2OoYMO15Jd0A==
+
+"@tailwindcss/oxide-darwin-x64@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.18.tgz"
+ integrity sha512-FL5oxr2xQsFrc3X9o1fjHKBYBMD1QZNyc1Xzw/h5Qu4XnEBi3dZn96HcHm41c/euGV+GRiXFfh2hUCyKi/e+yw==
+
+"@tailwindcss/oxide-freebsd-x64@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.18.tgz"
+ integrity sha512-Fj+RHgu5bDodmV1dM9yAxlfJwkkWvLiRjbhuO2LEtwtlYlBgiAT4x/j5wQr1tC3SANAgD+0YcmWVrj8R9trVMA==
+
+"@tailwindcss/oxide-linux-arm-gnueabihf@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.18.tgz"
+ integrity sha512-Fp+Wzk/Ws4dZn+LV2Nqx3IilnhH51YZoRaYHQsVq3RQvEl+71VGKFpkfHrLM/Li+kt5c0DJe/bHXK1eHgDmdiA==
+
+"@tailwindcss/oxide-linux-arm64-gnu@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.18.tgz"
+ integrity sha512-S0n3jboLysNbh55Vrt7pk9wgpyTTPD0fdQeh7wQfMqLPM/Hrxi+dVsLsPrycQjGKEQk85Kgbx+6+QnYNiHalnw==
+
+"@tailwindcss/oxide-linux-arm64-musl@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.18.tgz"
+ integrity sha512-1px92582HkPQlaaCkdRcio71p8bc8i/ap5807tPRDK/uw953cauQBT8c5tVGkOwrHMfc2Yh6UuxaH4vtTjGvHg==
+
+"@tailwindcss/oxide-linux-x64-gnu@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.18.tgz"
+ integrity sha512-v3gyT0ivkfBLoZGF9LyHmts0Isc8jHZyVcbzio6Wpzifg/+5ZJpDiRiUhDLkcr7f/r38SWNe7ucxmGW3j3Kb/g==
+
+"@tailwindcss/oxide-linux-x64-musl@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.18.tgz"
+ integrity sha512-bhJ2y2OQNlcRwwgOAGMY0xTFStt4/wyU6pvI6LSuZpRgKQwxTec0/3Scu91O8ir7qCR3AuepQKLU/kX99FouqQ==
+
+"@tailwindcss/oxide-wasm32-wasi@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.18.tgz"
+ integrity sha512-LffYTvPjODiP6PT16oNeUQJzNVyJl1cjIebq/rWWBF+3eDst5JGEFSc5cWxyRCJ0Mxl+KyIkqRxk1XPEs9x8TA==
+ dependencies:
+ "@emnapi/core" "^1.7.1"
+ "@emnapi/runtime" "^1.7.1"
+ "@emnapi/wasi-threads" "^1.1.0"
+ "@napi-rs/wasm-runtime" "^1.1.0"
+ "@tybys/wasm-util" "^0.10.1"
+ tslib "^2.4.0"
+
+"@tailwindcss/oxide-win32-arm64-msvc@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.18.tgz"
+ integrity sha512-HjSA7mr9HmC8fu6bdsZvZ+dhjyGCLdotjVOgLA2vEqxEBZaQo9YTX4kwgEvPCpRh8o4uWc4J/wEoFzhEmjvPbA==
+
+"@tailwindcss/oxide-win32-x64-msvc@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.18.tgz"
+ integrity sha512-bJWbyYpUlqamC8dpR7pfjA0I7vdF6t5VpUGMWRkXVE3AXgIZjYUYAK7II1GNaxR8J1SSrSrppRar8G++JekE3Q==
+
+"@tailwindcss/oxide@4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.18.tgz"
+ integrity sha512-EgCR5tTS5bUSKQgzeMClT6iCY3ToqE1y+ZB0AKldj809QXk1Y+3jB0upOYZrn9aGIzPtUsP7sX4QQ4XtjBB95A==
+ optionalDependencies:
+ "@tailwindcss/oxide-android-arm64" "4.1.18"
+ "@tailwindcss/oxide-darwin-arm64" "4.1.18"
+ "@tailwindcss/oxide-darwin-x64" "4.1.18"
+ "@tailwindcss/oxide-freebsd-x64" "4.1.18"
+ "@tailwindcss/oxide-linux-arm-gnueabihf" "4.1.18"
+ "@tailwindcss/oxide-linux-arm64-gnu" "4.1.18"
+ "@tailwindcss/oxide-linux-arm64-musl" "4.1.18"
+ "@tailwindcss/oxide-linux-x64-gnu" "4.1.18"
+ "@tailwindcss/oxide-linux-x64-musl" "4.1.18"
+ "@tailwindcss/oxide-wasm32-wasi" "4.1.18"
+ "@tailwindcss/oxide-win32-arm64-msvc" "4.1.18"
+ "@tailwindcss/oxide-win32-x64-msvc" "4.1.18"
+
+"@tailwindcss/postcss@^4.1.18":
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.18.tgz"
+ integrity sha512-Ce0GFnzAOuPyfV5SxjXGn0CubwGcuDB0zcdaPuCSzAa/2vII24JTkH+I6jcbXLb1ctjZMZZI6OjDaLPJQL1S0g==
+ dependencies:
+ "@alloc/quick-lru" "^5.2.0"
+ "@tailwindcss/node" "4.1.18"
+ "@tailwindcss/oxide" "4.1.18"
+ postcss "^8.4.41"
+ tailwindcss "4.1.18"
+
"@tanstack/react-virtual@3.11.2":
version "3.11.2"
resolved "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.11.2.tgz"
@@ -2482,6 +2799,13 @@
resolved "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.11.2.tgz"
integrity sha512-vTtpNt7mKCiZ1pwU9hfKPhpdVO2sVzFQsxoVBGtOSHxlrRRzYr8iQ2TlwbAcRYCcEiZ9ECAM8kBzH0v2+VzfKw==
+"@tybys/wasm-util@^0.10.1", "@tybys/wasm-util@^0.9.0":
+ version "0.9.0"
+ resolved "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.9.0.tgz"
+ integrity sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw==
+ dependencies:
+ tslib "^2.4.0"
+
"@types/d3-array@^3.0.3":
version "3.2.1"
resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.1.tgz"
@@ -2577,9 +2901,9 @@
"@types/lodash" "*"
"@types/lodash@*":
- version "4.17.16"
- resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.16.tgz"
- integrity sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==
+ version "4.17.21"
+ resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.21.tgz"
+ integrity sha512-FOvQ0YPD5NOfPgMzJihoT+Za5pdkDJWcbpuj1DjaKZIr/gxodQjY/uWEFlTNqW2ugXHUiL8lRQgw63dzKHZdeQ==
"@types/mdast@^4.0.0":
version "4.0.4"
@@ -2600,12 +2924,18 @@
dependencies:
undici-types "~6.21.0"
-"@types/react@^18.2.25 || ^19", "@types/react@>=18", "@types/react@19.1.5":
- version "19.1.5"
- resolved "https://registry.npmjs.org/@types/react/-/react-19.1.5.tgz"
- integrity sha512-piErsCVVbpMMT2r7wbawdZsq4xMvIAhQuac2gedQHysu1TZYEigE6pnFfgZT+/jQnrRuF5r+SHzuehFjfRjr4g==
+"@types/prop-types@*":
+ version "15.7.15"
+ resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz"
+ integrity sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==
+
+"@types/react@^18.2.0", "@types/react@^18.2.25 || ^19", "@types/react@>=18":
+ version "18.3.27"
+ resolved "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz"
+ integrity sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==
dependencies:
- csstype "^3.0.2"
+ "@types/prop-types" "*"
+ csstype "^3.2.2"
"@types/unist@*", "@types/unist@^3.0.0":
version "3.0.3"
@@ -2623,91 +2953,188 @@
integrity sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==
"@typescript-eslint/eslint-plugin@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz"
- integrity sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==
- dependencies:
- "@eslint-community/regexpp" "^4.10.0"
- "@typescript-eslint/scope-manager" "8.32.1"
- "@typescript-eslint/type-utils" "8.32.1"
- "@typescript-eslint/utils" "8.32.1"
- "@typescript-eslint/visitor-keys" "8.32.1"
- graphemer "^1.4.0"
- ignore "^7.0.0"
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.52.0.tgz"
+ integrity sha512-okqtOgqu2qmZJ5iN4TWlgfF171dZmx2FzdOv2K/ixL2LZWDStL8+JgQerI2sa8eAEfoydG9+0V96m7V+P8yE1Q==
+ dependencies:
+ "@eslint-community/regexpp" "^4.12.2"
+ "@typescript-eslint/scope-manager" "8.52.0"
+ "@typescript-eslint/type-utils" "8.52.0"
+ "@typescript-eslint/utils" "8.52.0"
+ "@typescript-eslint/visitor-keys" "8.52.0"
+ ignore "^7.0.5"
natural-compare "^1.4.0"
- ts-api-utils "^2.1.0"
-
-"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser@^8.0.0 || ^8.0.0-alpha.0":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz"
- integrity sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==
- dependencies:
- "@typescript-eslint/scope-manager" "8.32.1"
- "@typescript-eslint/types" "8.32.1"
- "@typescript-eslint/typescript-estree" "8.32.1"
- "@typescript-eslint/visitor-keys" "8.32.1"
- debug "^4.3.4"
-
-"@typescript-eslint/scope-manager@8.32.1":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz"
- integrity sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==
- dependencies:
- "@typescript-eslint/types" "8.32.1"
- "@typescript-eslint/visitor-keys" "8.32.1"
-
-"@typescript-eslint/type-utils@8.32.1":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz"
- integrity sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==
- dependencies:
- "@typescript-eslint/typescript-estree" "8.32.1"
- "@typescript-eslint/utils" "8.32.1"
- debug "^4.3.4"
- ts-api-utils "^2.1.0"
-
-"@typescript-eslint/types@8.32.1":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz"
- integrity sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==
-
-"@typescript-eslint/typescript-estree@8.32.1":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz"
- integrity sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==
- dependencies:
- "@typescript-eslint/types" "8.32.1"
- "@typescript-eslint/visitor-keys" "8.32.1"
- debug "^4.3.4"
- fast-glob "^3.3.2"
- is-glob "^4.0.3"
- minimatch "^9.0.4"
- semver "^7.6.0"
- ts-api-utils "^2.1.0"
-
-"@typescript-eslint/utils@8.32.1":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz"
- integrity sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==
- dependencies:
- "@eslint-community/eslint-utils" "^4.7.0"
- "@typescript-eslint/scope-manager" "8.32.1"
- "@typescript-eslint/types" "8.32.1"
- "@typescript-eslint/typescript-estree" "8.32.1"
-
-"@typescript-eslint/visitor-keys@8.32.1":
- version "8.32.1"
- resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz"
- integrity sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==
- dependencies:
- "@typescript-eslint/types" "8.32.1"
- eslint-visitor-keys "^4.2.0"
+ ts-api-utils "^2.4.0"
+
+"@typescript-eslint/parser@^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser@^8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.52.0.tgz"
+ integrity sha512-iIACsx8pxRnguSYhHiMn2PvhvfpopO9FXHyn1mG5txZIsAaB6F0KwbFnUQN3KCiG3Jcuad/Cao2FAs1Wp7vAyg==
+ dependencies:
+ "@typescript-eslint/scope-manager" "8.52.0"
+ "@typescript-eslint/types" "8.52.0"
+ "@typescript-eslint/typescript-estree" "8.52.0"
+ "@typescript-eslint/visitor-keys" "8.52.0"
+ debug "^4.4.3"
+
+"@typescript-eslint/project-service@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.52.0.tgz"
+ integrity sha512-xD0MfdSdEmeFa3OmVqonHi+Cciab96ls1UhIF/qX/O/gPu5KXD0bY9lu33jj04fjzrXHcuvjBcBC+D3SNSadaw==
+ dependencies:
+ "@typescript-eslint/tsconfig-utils" "^8.52.0"
+ "@typescript-eslint/types" "^8.52.0"
+ debug "^4.4.3"
+
+"@typescript-eslint/scope-manager@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.52.0.tgz"
+ integrity sha512-ixxqmmCcc1Nf8S0mS0TkJ/3LKcC8mruYJPOU6Ia2F/zUUR4pApW7LzrpU3JmtePbRUTes9bEqRc1Gg4iyRnDzA==
+ dependencies:
+ "@typescript-eslint/types" "8.52.0"
+ "@typescript-eslint/visitor-keys" "8.52.0"
+
+"@typescript-eslint/tsconfig-utils@^8.52.0", "@typescript-eslint/tsconfig-utils@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.52.0.tgz"
+ integrity sha512-jl+8fzr/SdzdxWJznq5nvoI7qn2tNYV/ZBAEcaFMVXf+K6jmXvAFrgo/+5rxgnL152f//pDEAYAhhBAZGrVfwg==
+
+"@typescript-eslint/type-utils@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.52.0.tgz"
+ integrity sha512-JD3wKBRWglYRQkAtsyGz1AewDu3mTc7NtRjR/ceTyGoPqmdS5oCdx/oZMWD5Zuqmo6/MpsYs0wp6axNt88/2EQ==
+ dependencies:
+ "@typescript-eslint/types" "8.52.0"
+ "@typescript-eslint/typescript-estree" "8.52.0"
+ "@typescript-eslint/utils" "8.52.0"
+ debug "^4.4.3"
+ ts-api-utils "^2.4.0"
+
+"@typescript-eslint/types@^8.52.0", "@typescript-eslint/types@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.52.0.tgz"
+ integrity sha512-LWQV1V4q9V4cT4H5JCIx3481iIFxH1UkVk+ZkGGAV1ZGcjGI9IoFOfg3O6ywz8QqCDEp7Inlg6kovMofsNRaGg==
+
+"@typescript-eslint/typescript-estree@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.52.0.tgz"
+ integrity sha512-XP3LClsCc0FsTK5/frGjolyADTh3QmsLp6nKd476xNI9CsSsLnmn4f0jrzNoAulmxlmNIpeXuHYeEQv61Q6qeQ==
+ dependencies:
+ "@typescript-eslint/project-service" "8.52.0"
+ "@typescript-eslint/tsconfig-utils" "8.52.0"
+ "@typescript-eslint/types" "8.52.0"
+ "@typescript-eslint/visitor-keys" "8.52.0"
+ debug "^4.4.3"
+ minimatch "^9.0.5"
+ semver "^7.7.3"
+ tinyglobby "^0.2.15"
+ ts-api-utils "^2.4.0"
+
+"@typescript-eslint/utils@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.52.0.tgz"
+ integrity sha512-wYndVMWkweqHpEpwPhwqE2lnD2DxC6WVLupU/DOt/0/v+/+iQbbzO3jOHjmBMnhu0DgLULvOaU4h4pwHYi2oRQ==
+ dependencies:
+ "@eslint-community/eslint-utils" "^4.9.1"
+ "@typescript-eslint/scope-manager" "8.52.0"
+ "@typescript-eslint/types" "8.52.0"
+ "@typescript-eslint/typescript-estree" "8.52.0"
+
+"@typescript-eslint/visitor-keys@8.52.0":
+ version "8.52.0"
+ resolved "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.52.0.tgz"
+ integrity sha512-ink3/Zofus34nmBsPjow63FP5M7IGff0RKAgqR6+CFpdk22M7aLwC9gOcLGYqr7MczLPzZVERW9hRog3O4n1sQ==
+ dependencies:
+ "@typescript-eslint/types" "8.52.0"
+ eslint-visitor-keys "^4.2.1"
"@ungap/structured-clone@^1.0.0", "@ungap/structured-clone@^1.2.0":
version "1.3.0"
resolved "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz"
integrity sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==
+"@unrs/resolver-binding-darwin-arm64@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-darwin-arm64/-/resolver-binding-darwin-arm64-1.7.2.tgz"
+ integrity sha512-vxtBno4xvowwNmO/ASL0Y45TpHqmNkAaDtz4Jqb+clmcVSSl8XCG/PNFFkGsXXXS6AMjP+ja/TtNCFFa1QwLRg==
+
+"@unrs/resolver-binding-darwin-x64@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-darwin-x64/-/resolver-binding-darwin-x64-1.7.2.tgz"
+ integrity sha512-qhVa8ozu92C23Hsmv0BF4+5Dyyd5STT1FolV4whNgbY6mj3kA0qsrGPe35zNR3wAN7eFict3s4Rc2dDTPBTuFQ==
+
+"@unrs/resolver-binding-freebsd-x64@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-freebsd-x64/-/resolver-binding-freebsd-x64-1.7.2.tgz"
+ integrity sha512-zKKdm2uMXqLFX6Ac7K5ElnnG5VIXbDlFWzg4WJ8CGUedJryM5A3cTgHuGMw1+P5ziV8CRhnSEgOnurTI4vpHpg==
+
+"@unrs/resolver-binding-linux-arm-gnueabihf@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-gnueabihf/-/resolver-binding-linux-arm-gnueabihf-1.7.2.tgz"
+ integrity sha512-8N1z1TbPnHH+iDS/42GJ0bMPLiGK+cUqOhNbMKtWJ4oFGzqSJk/zoXFzcQkgtI63qMcUI7wW1tq2usZQSb2jxw==
+
+"@unrs/resolver-binding-linux-arm-musleabihf@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm-musleabihf/-/resolver-binding-linux-arm-musleabihf-1.7.2.tgz"
+ integrity sha512-tjYzI9LcAXR9MYd9rO45m1s0B/6bJNuZ6jeOxo1pq1K6OBuRMMmfyvJYval3s9FPPGmrldYA3mi4gWDlWuTFGA==
+
+"@unrs/resolver-binding-linux-arm64-gnu@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-gnu/-/resolver-binding-linux-arm64-gnu-1.7.2.tgz"
+ integrity sha512-jon9M7DKRLGZ9VYSkFMflvNqu9hDtOCEnO2QAryFWgT6o6AXU8du56V7YqnaLKr6rAbZBWYsYpikF226v423QA==
+
+"@unrs/resolver-binding-linux-arm64-musl@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-arm64-musl/-/resolver-binding-linux-arm64-musl-1.7.2.tgz"
+ integrity sha512-c8Cg4/h+kQ63pL43wBNaVMmOjXI/X62wQmru51qjfTvI7kmCy5uHTJvK/9LrF0G8Jdx8r34d019P1DVJmhXQpA==
+
+"@unrs/resolver-binding-linux-ppc64-gnu@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-ppc64-gnu/-/resolver-binding-linux-ppc64-gnu-1.7.2.tgz"
+ integrity sha512-A+lcwRFyrjeJmv3JJvhz5NbcCkLQL6Mk16kHTNm6/aGNc4FwPHPE4DR9DwuCvCnVHvF5IAd9U4VIs/VvVir5lg==
+
+"@unrs/resolver-binding-linux-riscv64-gnu@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-gnu/-/resolver-binding-linux-riscv64-gnu-1.7.2.tgz"
+ integrity sha512-hQQ4TJQrSQW8JlPm7tRpXN8OCNP9ez7PajJNjRD1ZTHQAy685OYqPrKjfaMw/8LiHCt8AZ74rfUVHP9vn0N69Q==
+
+"@unrs/resolver-binding-linux-riscv64-musl@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-riscv64-musl/-/resolver-binding-linux-riscv64-musl-1.7.2.tgz"
+ integrity sha512-NoAGbiqrxtY8kVooZ24i70CjLDlUFI7nDj3I9y54U94p+3kPxwd2L692YsdLa+cqQ0VoqMWoehDFp21PKRUoIQ==
+
+"@unrs/resolver-binding-linux-s390x-gnu@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-s390x-gnu/-/resolver-binding-linux-s390x-gnu-1.7.2.tgz"
+ integrity sha512-KaZByo8xuQZbUhhreBTW+yUnOIHUsv04P8lKjQ5otiGoSJ17ISGYArc+4vKdLEpGaLbemGzr4ZeUbYQQsLWFjA==
+
+"@unrs/resolver-binding-linux-x64-gnu@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-gnu/-/resolver-binding-linux-x64-gnu-1.7.2.tgz"
+ integrity sha512-dEidzJDubxxhUCBJ/SHSMJD/9q7JkyfBMT77Px1npl4xpg9t0POLvnWywSk66BgZS/b2Hy9Y1yFaoMTFJUe9yg==
+
+"@unrs/resolver-binding-linux-x64-musl@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-linux-x64-musl/-/resolver-binding-linux-x64-musl-1.7.2.tgz"
+ integrity sha512-RvP+Ux3wDjmnZDT4XWFfNBRVG0fMsc+yVzNFUqOflnDfZ9OYujv6nkh+GOr+watwrW4wdp6ASfG/e7bkDradsw==
+
+"@unrs/resolver-binding-wasm32-wasi@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-wasm32-wasi/-/resolver-binding-wasm32-wasi-1.7.2.tgz"
+ integrity sha512-y797JBmO9IsvXVRCKDXOxjyAE4+CcZpla2GSoBQ33TVb3ILXuFnMrbR/QQZoauBYeOFuu4w3ifWLw52sdHGz6g==
+ dependencies:
+ "@napi-rs/wasm-runtime" "^0.2.9"
+
+"@unrs/resolver-binding-win32-arm64-msvc@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-arm64-msvc/-/resolver-binding-win32-arm64-msvc-1.7.2.tgz"
+ integrity sha512-gtYTh4/VREVSLA+gHrfbWxaMO/00y+34htY7XpioBTy56YN2eBjkPrY1ML1Zys89X3RJDKVaogzwxlM1qU7egg==
+
+"@unrs/resolver-binding-win32-ia32-msvc@1.7.2":
+ version "1.7.2"
+ resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-ia32-msvc/-/resolver-binding-win32-ia32-msvc-1.7.2.tgz"
+ integrity sha512-Ywv20XHvHTDRQs12jd3MY8X5C8KLjDbg/jyaal/QLKx3fAShhJyD4blEANInsjxW3P7isHx1Blt56iUDDJO3jg==
+
"@unrs/resolver-binding-win32-x64-msvc@1.7.2":
version "1.7.2"
resolved "https://registry.npmjs.org/@unrs/resolver-binding-win32-x64-msvc/-/resolver-binding-win32-x64-msvc-1.7.2.tgz"
@@ -2739,9 +3166,9 @@ ansi-regex@^5.0.1:
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
ansi-regex@^6.0.1:
- version "6.1.0"
- resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz"
- integrity sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==
+ version "6.2.2"
+ resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz"
+ integrity sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
version "4.3.0"
@@ -2751,27 +3178,9 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0:
color-convert "^2.0.1"
ansi-styles@^6.1.0:
- version "6.2.1"
- resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz"
- integrity sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==
-
-any-promise@^1.0.0:
- version "1.3.0"
- resolved "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz"
- integrity sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==
-
-anymatch@~3.1.2:
- version "3.1.3"
- resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz"
- integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
- dependencies:
- normalize-path "^3.0.0"
- picomatch "^2.0.4"
-
-arg@^5.0.2:
- version "5.0.2"
- resolved "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz"
- integrity sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==
+ version "6.2.3"
+ resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz"
+ integrity sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==
argparse@^2.0.1:
version "2.0.1"
@@ -2899,13 +3308,13 @@ axe-core@^4.10.0:
resolved "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz"
integrity sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==
-axios@^1.7.9:
- version "1.9.0"
- resolved "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz"
- integrity sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==
+axios@1.13.2:
+ version "1.13.2"
+ resolved "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz"
+ integrity sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==
dependencies:
follow-redirects "^1.15.6"
- form-data "^4.0.0"
+ form-data "^4.0.4"
proxy-from-env "^1.1.0"
axobject-query@^4.1.0:
@@ -2923,40 +3332,20 @@ balanced-match@^1.0.0:
resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
-binary-extensions@^2.0.0:
- version "2.3.0"
- resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz"
- integrity sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==
-
-brace-expansion@^1.1.7:
- version "1.1.11"
- resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz"
- integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
- dependencies:
- balanced-match "^1.0.0"
- concat-map "0.0.1"
-
-brace-expansion@^2.0.1:
- version "2.0.1"
- resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz"
- integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==
+brace-expansion@^2.0.2:
+ version "2.0.2"
+ resolved "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz"
+ integrity sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==
dependencies:
balanced-match "^1.0.0"
-braces@^3.0.3, braces@~3.0.2:
+braces@^3.0.3:
version "3.0.3"
resolved "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz"
integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies:
fill-range "^7.1.1"
-busboy@1.6.0:
- version "1.6.0"
- resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
- integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
- dependencies:
- streamsearch "^1.1.0"
-
call-bind-apply-helpers@^1.0.0, call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
@@ -2988,11 +3377,6 @@ callsites@^3.0.0:
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==
-camelcase-css@^2.0.1:
- version "2.0.1"
- resolved "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz"
- integrity sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==
-
caniuse-lite@^1.0.30001579:
version "1.0.30001718"
resolved "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001718.tgz"
@@ -3031,21 +3415,6 @@ character-reference-invalid@^2.0.0:
resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz"
integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
-chokidar@^3.6.0:
- version "3.6.0"
- resolved "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz"
- integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==
- dependencies:
- anymatch "~3.1.2"
- braces "~3.0.2"
- glob-parent "~5.1.2"
- is-binary-path "~2.1.0"
- is-glob "~4.0.1"
- normalize-path "~3.0.0"
- readdirp "~3.6.0"
- optionalDependencies:
- fsevents "~2.3.2"
-
client-only@0.0.1:
version "0.0.1"
resolved "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz"
@@ -3056,12 +3425,7 @@ clsx@^1.2.1:
resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz"
integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==
-clsx@^2.0.0:
- version "2.1.1"
- resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
- integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
-
-clsx@^2.1.1:
+clsx@^2.0.0, clsx@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==
@@ -3111,21 +3475,11 @@ comma-separated-tokens@^2.0.0:
resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz"
integrity sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==
-commander@^4.0.0:
- version "4.1.1"
- resolved "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz"
- integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
-
compute-scroll-into-view@^3.0.2:
version "3.1.1"
resolved "https://registry.npmjs.org/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz"
integrity sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==
-concat-map@0.0.1:
- version "0.0.1"
- resolved "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz"
- integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
-
cross-spawn@^7.0.2, cross-spawn@^7.0.6:
version "7.0.6"
resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz"
@@ -3135,15 +3489,10 @@ cross-spawn@^7.0.2, cross-spawn@^7.0.6:
shebang-command "^2.0.0"
which "^2.0.1"
-cssesc@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz"
- integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
-
-csstype@^3.0.2:
- version "3.1.3"
- resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz"
- integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==
+csstype@^3.2.2:
+ version "3.2.3"
+ resolved "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz"
+ integrity sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==
d3-array@^3.1.6, "d3-array@2 - 3", "d3-array@2.10.0 - 3":
version "3.2.4"
@@ -3255,10 +3604,10 @@ debug@^3.2.7:
dependencies:
ms "^2.1.1"
-debug@^4.0.0, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0:
- version "4.4.0"
- resolved "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz"
- integrity sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==
+debug@^4.0.0, debug@^4.3.1, debug@^4.3.2, debug@^4.4.0, debug@^4.4.3:
+ version "4.4.3"
+ resolved "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz"
+ integrity sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==
dependencies:
ms "^2.1.3"
@@ -3317,10 +3666,10 @@ dequal@^2.0.0:
resolved "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz"
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
-detect-libc@^2.0.3:
- version "2.0.4"
- resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz"
- integrity sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==
+detect-libc@^2.0.3, detect-libc@^2.1.2:
+ version "2.1.2"
+ resolved "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz"
+ integrity sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==
devlop@^1.0.0, devlop@^1.1.0:
version "1.1.0"
@@ -3329,16 +3678,6 @@ devlop@^1.0.0, devlop@^1.1.0:
dependencies:
dequal "^2.0.0"
-didyoumean@^1.2.2:
- version "1.2.2"
- resolved "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz"
- integrity sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==
-
-dlv@^1.1.3:
- version "1.1.3"
- resolved "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz"
- integrity sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==
-
doctrine@^2.1.0:
version "2.1.0"
resolved "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz"
@@ -3377,6 +3716,14 @@ emoji-regex@^9.2.2:
resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz"
integrity sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==
+enhanced-resolve@^5.18.3:
+ version "5.18.4"
+ resolved "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz"
+ integrity sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==
+ dependencies:
+ graceful-fs "^4.2.4"
+ tapable "^2.2.0"
+
es-abstract@^1.17.5, es-abstract@^1.23.2, es-abstract@^1.23.3, es-abstract@^1.23.5, es-abstract@^1.23.6, es-abstract@^1.23.9:
version "1.23.9"
resolved "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz"
@@ -3514,12 +3861,12 @@ escape-string-regexp@^5.0.0:
resolved "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz"
integrity sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==
-eslint-config-next@15.0.2:
- version "15.0.2"
- resolved "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.0.2.tgz"
- integrity sha512-N8o6cyUXzlMmQbdc2Kc83g1qomFi3ITqrAZfubipVKET2uR2mCStyGRcx/r8WiAIVMul2KfwRiCHBkTpBvGBmA==
+eslint-config-next@15.5.9:
+ version "15.5.9"
+ resolved "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.5.9.tgz"
+ integrity sha512-852JYI3NkFNzW8CqsMhI0K2CDRxTObdZ2jQJj5CtpEaOkYHn13107tHpNuD/h0WRpU4FAbCdUaxQsrfBtNK9Kw==
dependencies:
- "@next/eslint-plugin-next" "15.0.2"
+ "@next/eslint-plugin-next" "15.5.9"
"@rushstack/eslint-patch" "^1.10.3"
"@typescript-eslint/eslint-plugin" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
"@typescript-eslint/parser" "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0"
@@ -3527,9 +3874,14 @@ eslint-config-next@15.0.2:
eslint-import-resolver-typescript "^3.5.2"
eslint-plugin-import "^2.31.0"
eslint-plugin-jsx-a11y "^6.10.0"
- eslint-plugin-react "^7.35.0"
+ eslint-plugin-react "^7.37.0"
eslint-plugin-react-hooks "^5.0.0"
+eslint-config-prettier@^9.1.0:
+ version "9.1.2"
+ resolved "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-9.1.2.tgz"
+ integrity sha512-iI1f+D2ViGn+uvv5HuHVUamg8ll4tN+JRHGc6IJi4TP9Kl976C57fzPXgseXNs8v0iA8aSJpHsTWjDb9QJamGQ==
+
eslint-import-resolver-node@^0.3.6, eslint-import-resolver-node@^0.3.9:
version "0.3.9"
resolved "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz"
@@ -3610,7 +3962,7 @@ eslint-plugin-react-hooks@^5.0.0:
resolved "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz"
integrity sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==
-eslint-plugin-react@^7.35.0:
+eslint-plugin-react@^7.37.0, eslint-plugin-react@^7.37.5:
version "7.37.5"
resolved "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz"
integrity sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==
@@ -3647,12 +3999,12 @@ eslint-visitor-keys@^3.4.1, eslint-visitor-keys@^3.4.3:
resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz"
integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==
-eslint-visitor-keys@^4.2.0:
- version "4.2.0"
- resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz"
- integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==
+eslint-visitor-keys@^4.2.1:
+ version "4.2.1"
+ resolved "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz"
+ integrity sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==
-eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.23.0 || ^8.0.0 || ^9.0.0", eslint@^8, "eslint@^8.57.0 || ^9.0.0":
+eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0", "eslint@^6.0.0 || ^7.0.0 || >=8.0.0", "eslint@^7.23.0 || ^8.0.0 || ^9.0.0", eslint@^8, "eslint@^8.57.0 || ^9.0.0", eslint@>=7.0.0:
version "8.57.1"
resolved "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz"
integrity sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==
@@ -3749,17 +4101,6 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
-fast-glob@^3.3.2:
- version "3.3.3"
- resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz"
- integrity sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==
- dependencies:
- "@nodelib/fs.stat" "^2.0.2"
- "@nodelib/fs.walk" "^1.2.3"
- glob-parent "^5.1.2"
- merge2 "^1.3.0"
- micromatch "^4.0.8"
-
fast-glob@3.3.1:
version "3.3.1"
resolved "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz"
@@ -3788,10 +4129,10 @@ fastq@^1.6.0:
dependencies:
reusify "^1.0.4"
-fdir@^6.4.4:
- version "6.4.4"
- resolved "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz"
- integrity sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==
+fdir@^6.5.0:
+ version "6.5.0"
+ resolved "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz"
+ integrity sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==
file-entry-cache@^6.0.1:
version "6.0.1"
@@ -3854,30 +4195,26 @@ foreground-child@^3.1.0:
cross-spawn "^7.0.6"
signal-exit "^4.0.1"
-form-data@^4.0.0:
- version "4.0.2"
- resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz"
- integrity sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==
+form-data@^4.0.4:
+ version "4.0.5"
+ resolved "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz"
+ integrity sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==
dependencies:
asynckit "^0.4.0"
combined-stream "^1.0.8"
es-set-tostringtag "^2.1.0"
+ hasown "^2.0.2"
mime-types "^2.1.12"
-framer-motion@^11.16.1, "framer-motion@>=11.5.6 || >=12.0.0-alpha.1":
- version "11.18.2"
- resolved "https://registry.npmjs.org/framer-motion/-/framer-motion-11.18.2.tgz"
- integrity sha512-5F5Och7wrvtLVElIpclDT0CBzMVg3dL22B64aZwHtsIY8RB4mXICLrkajK4G9R+ieSAGcgrLeae2SeUTg2pr6w==
+framer-motion@^12.24.7, "framer-motion@>=11.5.6 || >=12.0.0-alpha.1":
+ version "12.24.7"
+ resolved "https://registry.npmjs.org/framer-motion/-/framer-motion-12.24.7.tgz"
+ integrity sha512-EolFLm7NdEMhWO/VTMZ0LlR4fLHGDiJItTx3i8dlyQooOOBoYAaysK4paGD4PrwqnoDdeDOS+TxnSBIAnNHs3w==
dependencies:
- motion-dom "^11.18.1"
- motion-utils "^11.18.1"
+ motion-dom "^12.24.3"
+ motion-utils "^12.23.28"
tslib "^2.4.0"
-fs.realpath@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz"
- integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
-
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
@@ -3954,17 +4291,10 @@ glob-parent@^6.0.2:
dependencies:
is-glob "^4.0.3"
-glob-parent@~5.1.2:
- version "5.1.2"
- resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz"
- integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
- dependencies:
- is-glob "^4.0.1"
-
-glob@^10.3.10:
- version "10.4.5"
- resolved "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz"
- integrity sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==
+glob@^10.5.0:
+ version "10.5.0"
+ resolved "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz"
+ integrity sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==
dependencies:
foreground-child "^3.1.0"
jackspeak "^3.1.2"
@@ -3973,18 +4303,6 @@ glob@^10.3.10:
package-json-from-dist "^1.0.0"
path-scurry "^1.11.1"
-glob@^7.1.3:
- version "7.2.3"
- resolved "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz"
- integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
- dependencies:
- fs.realpath "^1.0.0"
- inflight "^1.0.4"
- inherits "2"
- minimatch "^3.1.1"
- once "^1.3.0"
- path-is-absolute "^1.0.0"
-
globals@^13.19.0:
version "13.24.0"
resolved "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz"
@@ -4005,15 +4323,20 @@ gopd@^1.0.1, gopd@^1.2.0:
resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
+graceful-fs@^4.2.4:
+ version "4.2.11"
+ resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
+ integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
+
graphemer@^1.4.0:
version "1.4.0"
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
-gsap@^3.12.7:
- version "3.13.0"
- resolved "https://registry.npmjs.org/gsap/-/gsap-3.13.0.tgz"
- integrity sha512-QL7MJ2WMjm1PHWsoFrAQH/J8wUeqZvMtHO58qdekHpCfhvhSL4gSiz6vJf5EeMP0LOn3ZCprL2ki/gjED8ghVw==
+gsap@^3.14.2:
+ version "3.14.2"
+ resolved "https://registry.npmjs.org/gsap/-/gsap-3.14.2.tgz"
+ integrity sha512-P8/mMxVLU7o4+55+1TCnQrPmgjPKnwkzkXOK1asnR9Jg2lna4tEY5qBJjMmAaOBDDZWtlRjBXjLa0w53G/uBLA==
hangul-js@^0.2.6:
version "0.2.6"
@@ -4101,10 +4424,10 @@ ignore@^5.2.0:
resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz"
integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==
-ignore@^7.0.0:
- version "7.0.4"
- resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz"
- integrity sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==
+ignore@^7.0.5:
+ version "7.0.5"
+ resolved "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz"
+ integrity sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==
immer@^10.0.3, immer@^10.1.1:
version "10.1.3"
@@ -4124,19 +4447,6 @@ imurmurhash@^0.1.4:
resolved "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz"
integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==
-inflight@^1.0.4:
- version "1.0.6"
- resolved "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz"
- integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
- dependencies:
- once "^1.3.0"
- wrappy "1"
-
-inherits@2:
- version "2.0.4"
- resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
- integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
-
inline-style-parser@0.2.4:
version "0.2.4"
resolved "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz"
@@ -4223,13 +4533,6 @@ is-bigint@^1.1.0:
dependencies:
has-bigints "^1.0.2"
-is-binary-path@~2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz"
- integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==
- dependencies:
- binary-extensions "^2.0.0"
-
is-boolean-object@^1.2.1:
version "1.2.2"
resolved "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz"
@@ -4306,7 +4609,7 @@ is-generator-function@^1.0.10:
has-tostringtag "^1.0.2"
safe-regex-test "^1.1.0"
-is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1:
+is-glob@^4.0.0, is-glob@^4.0.1, is-glob@^4.0.3:
version "4.0.3"
resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==
@@ -4443,10 +4746,10 @@ jackspeak@^3.1.2:
optionalDependencies:
"@pkgjs/parseargs" "^0.11.0"
-jiti@^1.21.6:
- version "1.21.7"
- resolved "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz"
- integrity sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==
+jiti@^2.6.1:
+ version "2.6.1"
+ resolved "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz"
+ integrity sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==
"js-tokens@^3.0.0 || ^4.0.0":
version "4.0.0"
@@ -4454,9 +4757,9 @@ jiti@^1.21.6:
integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==
js-yaml@^4.1.0:
- version "4.1.0"
- resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz"
- integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==
+ version "4.1.1"
+ resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz"
+ integrity sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==
dependencies:
argparse "^2.0.1"
@@ -4524,15 +4827,79 @@ levn@^0.4.1:
prelude-ls "^1.2.1"
type-check "~0.4.0"
-lilconfig@^3.0.0, lilconfig@^3.1.3:
- version "3.1.3"
- resolved "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz"
- integrity sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==
-
-lines-and-columns@^1.1.6:
- version "1.2.4"
- resolved "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz"
- integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==
+lightningcss-android-arm64@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.30.2.tgz"
+ integrity sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==
+
+lightningcss-darwin-arm64@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.2.tgz"
+ integrity sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==
+
+lightningcss-darwin-x64@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.2.tgz"
+ integrity sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==
+
+lightningcss-freebsd-x64@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.2.tgz"
+ integrity sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==
+
+lightningcss-linux-arm-gnueabihf@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.2.tgz"
+ integrity sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==
+
+lightningcss-linux-arm64-gnu@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.2.tgz"
+ integrity sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==
+
+lightningcss-linux-arm64-musl@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.2.tgz"
+ integrity sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==
+
+lightningcss-linux-x64-gnu@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.2.tgz"
+ integrity sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==
+
+lightningcss-linux-x64-musl@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.2.tgz"
+ integrity sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==
+
+lightningcss-win32-arm64-msvc@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.2.tgz"
+ integrity sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==
+
+lightningcss-win32-x64-msvc@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.2.tgz"
+ integrity sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==
+
+lightningcss@1.30.2:
+ version "1.30.2"
+ resolved "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.2.tgz"
+ integrity sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==
+ dependencies:
+ detect-libc "^2.0.3"
+ optionalDependencies:
+ lightningcss-android-arm64 "1.30.2"
+ lightningcss-darwin-arm64 "1.30.2"
+ lightningcss-darwin-x64 "1.30.2"
+ lightningcss-freebsd-x64 "1.30.2"
+ lightningcss-linux-arm-gnueabihf "1.30.2"
+ lightningcss-linux-arm64-gnu "1.30.2"
+ lightningcss-linux-arm64-musl "1.30.2"
+ lightningcss-linux-x64-gnu "1.30.2"
+ lightningcss-linux-x64-musl "1.30.2"
+ lightningcss-win32-arm64-msvc "1.30.2"
+ lightningcss-win32-x64-msvc "1.30.2"
locate-path@^6.0.0:
version "6.0.0"
@@ -4563,10 +4930,17 @@ lru-cache@^10.2.0:
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz"
integrity sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==
-lucide-react@^0.507.0:
- version "0.507.0"
- resolved "https://registry.npmjs.org/lucide-react/-/lucide-react-0.507.0.tgz"
- integrity sha512-XfgE6gvAHwAtnbUvWiTTHx4S3VGR+cUJHEc0vrh9Ogu672I1Tue2+Cp/8JJqpytgcBHAB1FVI297W4XGNwc2dQ==
+lucide-react@^0.562.0:
+ version "0.562.0"
+ resolved "https://registry.npmjs.org/lucide-react/-/lucide-react-0.562.0.tgz"
+ integrity sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==
+
+magic-string@^0.30.21:
+ version "0.30.21"
+ resolved "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz"
+ integrity sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==
+ dependencies:
+ "@jridgewell/sourcemap-codec" "^1.5.5"
markdown-table@^3.0.0:
version "3.0.4"
@@ -4729,10 +5103,10 @@ mdast-util-phrasing@^4.0.0:
"@types/mdast" "^4.0.0"
unist-util-is "^6.0.0"
-mdast-util-to-hast@^13.0.0:
- version "13.2.0"
- resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz"
- integrity sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==
+mdast-util-to-hast@^13.2.1:
+ version "13.2.1"
+ resolved "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.1.tgz"
+ integrity sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==
dependencies:
"@types/hast" "^3.0.0"
"@types/mdast" "^4.0.0"
@@ -5044,7 +5418,7 @@ micromark@^4.0.0:
micromark-util-symbol "^2.0.0"
micromark-util-types "^2.0.0"
-micromatch@^4.0.4, micromatch@^4.0.8:
+micromatch@^4.0.4:
version "4.0.8"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz"
integrity sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==
@@ -5064,7 +5438,7 @@ mime-types@^2.1.12:
dependencies:
mime-db "1.52.0"
-minimatch@^3.0.5, minimatch@^3.1.1, minimatch@^3.1.2:
+minimatch@^3.0.5, minimatch@^3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
@@ -5078,6 +5452,13 @@ minimatch@^9.0.4:
dependencies:
brace-expansion "^2.0.1"
+minimatch@^9.0.5:
+ version "9.0.5"
+ resolved "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz"
+ integrity sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==
+ dependencies:
+ brace-expansion "^2.0.1"
+
minimist@^1.2.0, minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz"
@@ -5088,32 +5469,23 @@ minimist@^1.2.0, minimist@^1.2.6:
resolved "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz"
integrity sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==
-motion-dom@^11.18.1:
- version "11.18.1"
- resolved "https://registry.npmjs.org/motion-dom/-/motion-dom-11.18.1.tgz"
- integrity sha512-g76KvA001z+atjfxczdRtw/RXOM3OMSdd1f4DL77qCTF/+avrRJiawSG4yDibEQ215sr9kpinSlX2pCTJ9zbhw==
+motion-dom@^12.24.3:
+ version "12.24.3"
+ resolved "https://registry.npmjs.org/motion-dom/-/motion-dom-12.24.3.tgz"
+ integrity sha512-ZjMZCwhTglim0LM64kC1iFdm4o+2P9IKk3rl/Nb4RKsb5p4O9HJ1C2LWZXOFdsRtp6twpqWRXaFKOduF30ntow==
dependencies:
- motion-utils "^11.18.1"
+ motion-utils "^12.23.28"
-motion-utils@^11.18.1:
- version "11.18.1"
- resolved "https://registry.npmjs.org/motion-utils/-/motion-utils-11.18.1.tgz"
- integrity sha512-49Kt+HKjtbJKLtgO/LKj9Ld+6vw9BjH5d9sc40R/kVyH8GLAXgT42M2NnuPcJNuA3s9ZfZBUcwIgpmZWGEE+hA==
+motion-utils@^12.23.28:
+ version "12.23.28"
+ resolved "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.28.tgz"
+ integrity sha512-0W6cWd5Okoyf8jmessVK3spOmbyE0yTdNKujHctHH9XdAE4QDuZ1/LjSXC68rrhsJU+TkzXURC5OdSWh9ibOwQ==
ms@^2.1.1, ms@^2.1.3:
version "2.1.3"
resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
-mz@^2.7.0:
- version "2.7.0"
- resolved "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz"
- integrity sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==
- dependencies:
- any-promise "^1.0.0"
- object-assign "^4.0.1"
- thenify-all "^1.0.0"
-
nanoid@^3.3.6, nanoid@^3.3.8:
version "3.3.11"
resolved "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz"
@@ -5129,44 +5501,32 @@ natural-compare@^1.4.0:
resolved "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz"
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
-next@>=15.2.3:
- version "15.3.2"
- resolved "https://registry.npmjs.org/next/-/next-15.3.2.tgz"
- integrity sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ==
+next@15.5.9:
+ version "15.5.9"
+ resolved "https://registry.npmjs.org/next/-/next-15.5.9.tgz"
+ integrity sha512-agNLK89seZEtC5zUHwtut0+tNrc0Xw4FT/Dg+B/VLEo9pAcS9rtTKpek3V6kVcVwsB2YlqMaHdfZL4eLEVYuCg==
dependencies:
- "@next/env" "15.3.2"
- "@swc/counter" "0.1.3"
+ "@next/env" "15.5.9"
"@swc/helpers" "0.5.15"
- busboy "1.6.0"
caniuse-lite "^1.0.30001579"
postcss "8.4.31"
styled-jsx "5.1.6"
optionalDependencies:
- "@next/swc-darwin-arm64" "15.3.2"
- "@next/swc-darwin-x64" "15.3.2"
- "@next/swc-linux-arm64-gnu" "15.3.2"
- "@next/swc-linux-arm64-musl" "15.3.2"
- "@next/swc-linux-x64-gnu" "15.3.2"
- "@next/swc-linux-x64-musl" "15.3.2"
- "@next/swc-win32-arm64-msvc" "15.3.2"
- "@next/swc-win32-x64-msvc" "15.3.2"
- sharp "^0.34.1"
-
-normalize-path@^3.0.0, normalize-path@~3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
- integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==
-
-object-assign@^4.0.1, object-assign@^4.1.1:
+ "@next/swc-darwin-arm64" "15.5.7"
+ "@next/swc-darwin-x64" "15.5.7"
+ "@next/swc-linux-arm64-gnu" "15.5.7"
+ "@next/swc-linux-arm64-musl" "15.5.7"
+ "@next/swc-linux-x64-gnu" "15.5.7"
+ "@next/swc-linux-x64-musl" "15.5.7"
+ "@next/swc-win32-arm64-msvc" "15.5.7"
+ "@next/swc-win32-x64-msvc" "15.5.7"
+ sharp "^0.34.3"
+
+object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
-object-hash@^3.0.0:
- version "3.0.0"
- resolved "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz"
- integrity sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==
-
object-inspect@^1.13.3:
version "1.13.4"
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz"
@@ -5228,13 +5588,6 @@ object.values@^1.1.6, object.values@^1.2.0, object.values@^1.2.1:
define-properties "^1.2.1"
es-object-atoms "^1.0.0"
-once@^1.3.0:
- version "1.4.0"
- resolved "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
- integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
- dependencies:
- wrappy "1"
-
optionator@^0.9.3:
version "0.9.4"
resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz"
@@ -5300,11 +5653,6 @@ path-exists@^4.0.0:
resolved "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz"
integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==
-path-is-absolute@^1.0.0:
- version "1.0.1"
- resolved "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz"
- integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
-
path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz"
@@ -5328,76 +5676,22 @@ picocolors@^1.0.0, picocolors@^1.1.1:
resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz"
integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==
-picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
+picomatch@^2.3.1:
version "2.3.1"
resolved "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz"
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
-"picomatch@^3 || ^4", picomatch@^4.0.2:
- version "4.0.2"
- resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz"
- integrity sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==
-
-pify@^2.3.0:
- version "2.3.0"
- resolved "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz"
- integrity sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==
-
-pirates@^4.0.1:
- version "4.0.7"
- resolved "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz"
- integrity sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==
+"picomatch@^3 || ^4", picomatch@^4.0.3:
+ version "4.0.3"
+ resolved "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz"
+ integrity sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==
possible-typed-array-names@^1.0.0:
version "1.1.0"
resolved "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz"
integrity sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==
-postcss-import@^15.1.0:
- version "15.1.0"
- resolved "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz"
- integrity sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==
- dependencies:
- postcss-value-parser "^4.0.0"
- read-cache "^1.0.0"
- resolve "^1.1.7"
-
-postcss-js@^4.0.1:
- version "4.0.1"
- resolved "https://registry.npmjs.org/postcss-js/-/postcss-js-4.0.1.tgz"
- integrity sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==
- dependencies:
- camelcase-css "^2.0.1"
-
-postcss-load-config@^4.0.2:
- version "4.0.2"
- resolved "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-4.0.2.tgz"
- integrity sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==
- dependencies:
- lilconfig "^3.0.0"
- yaml "^2.3.4"
-
-postcss-nested@^6.2.0:
- version "6.2.0"
- resolved "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz"
- integrity sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==
- dependencies:
- postcss-selector-parser "^6.1.1"
-
-postcss-selector-parser@^6.1.1, postcss-selector-parser@^6.1.2:
- version "6.1.2"
- resolved "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz"
- integrity sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==
- dependencies:
- cssesc "^3.0.0"
- util-deprecate "^1.0.2"
-
-postcss-value-parser@^4.0.0:
- version "4.2.0"
- resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz"
- integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
-
-postcss@^8, postcss@^8.0.0, postcss@^8.2.14, postcss@^8.4.21, postcss@^8.4.47, postcss@>=8.0.9:
+postcss@^8, postcss@^8.4.41:
version "8.5.3"
resolved "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz"
integrity sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==
@@ -5420,6 +5714,11 @@ prelude-ls@^1.2.1:
resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz"
integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==
+prettier@^3.5.1:
+ version "3.8.1"
+ resolved "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz"
+ integrity sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==
+
prop-types@^15.8.1:
version "15.8.1"
resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz"
@@ -5449,7 +5748,7 @@ queue-microtask@^1.2.2:
resolved "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz"
integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
-"react-dom@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^16.8 || ^17 || ^18 || ^19", "react-dom@^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom@^18.0.0 || ^19.0.0", react-dom@^18.2.0, "react-dom@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react-dom@>=18, "react-dom@>=18 || >=19.0.0-rc.0":
+"react-dom@^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^16.8 || ^17 || ^18 || ^19", "react-dom@^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react-dom@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react-dom@^18.0.0 || ^19.0.0", "react-dom@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react-dom@>=18, "react-dom@>=18 || >=19.0.0-rc.0", react-dom@18.3.1:
version "18.3.1"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
integrity sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==
@@ -5457,12 +5756,10 @@ queue-microtask@^1.2.2:
loose-envify "^1.1.0"
scheduler "^0.23.2"
-react-error-boundary@^6.0.0:
- version "6.0.0"
- resolved "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz"
- integrity sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==
- dependencies:
- "@babel/runtime" "^7.12.5"
+react-error-boundary@^6.0.2:
+ version "6.0.2"
+ resolved "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.2.tgz"
+ integrity sha512-yvWErn55ag/ywZEFqYpXYX9rxIDPIabXIX25F184KY3F5Szk2x/cVieOflw5R47ltN3KzWOw82Lmlb4vNjyn9A==
react-icons@^5.4.0:
version "5.5.0"
@@ -5508,31 +5805,17 @@ react-textarea-autosize@^8.5.3:
use-composed-ref "^1.3.0"
use-latest "^1.2.1"
-react@*, "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17 || ^18 || ^19", "react@^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react@^16.9.0 || ^17.0.0 || ^18 || ^19", "react@^18.0 || ^19", "react@^18.0.0 || ^19.0.0", react@^18.2.0, "react@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react@^18.3.1, "react@>= 16 || ^19.0.0-rc", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0", react@>=16.13.1, react@>=18, "react@>=18 || >=19.0.0-rc.0":
+react@*, "react@^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8 || ^17 || ^18 || ^19", "react@^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", "react@^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "react@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1", "react@^16.9.0 || ^17.0.0 || ^18 || ^19", "react@^18.0 || ^19", "react@^18.0.0 || ^19.0.0", "react@^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", react@^18.3.1, "react@>= 16 || ^19.0.0-rc", "react@>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0", react@>=18, "react@>=18 || >=19.0.0-rc.0", react@18.3.1:
version "18.3.1"
resolved "https://registry.npmjs.org/react/-/react-18.3.1.tgz"
integrity sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==
dependencies:
loose-envify "^1.1.0"
-read-cache@^1.0.0:
- version "1.0.0"
- resolved "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz"
- integrity sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==
- dependencies:
- pify "^2.3.0"
-
-readdirp@~3.6.0:
+recharts@^3.6.0:
version "3.6.0"
- resolved "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz"
- integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==
- dependencies:
- picomatch "^2.2.1"
-
-recharts@^3.1.2:
- version "3.1.2"
- resolved "https://registry.npmjs.org/recharts/-/recharts-3.1.2.tgz"
- integrity sha512-vhNbYwaxNbk/IATK0Ki29k3qvTkGqwvCgyQAQ9MavvvBwjvKnMTswdbklJpcOAoMPN/qxF3Lyqob0zO+ZXkZ4g==
+ resolved "https://registry.npmjs.org/recharts/-/recharts-3.6.0.tgz"
+ integrity sha512-L5bjxvQRAe26RlToBAziKUB7whaGKEwD3znoM6fz3DrTowCIC/FnJYnuq1GEzB8Zv2kdTfaxQfi5GoH0tBinyg==
dependencies:
"@reduxjs/toolkit" "1.x.x || 2.x.x"
clsx "^2.1.1"
@@ -5648,7 +5931,7 @@ resolve-pkg-maps@^1.0.0:
resolved "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz"
integrity sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==
-resolve@^1.1.7, resolve@^1.22.4, resolve@^1.22.8:
+resolve@^1.22.4:
version "1.22.10"
resolved "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz"
integrity sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==
@@ -5732,10 +6015,10 @@ semver@^6.3.1:
resolved "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
-semver@^7.6.0, semver@^7.7.1:
- version "7.7.2"
- resolved "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz"
- integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==
+semver@^7.7.1, semver@^7.7.3:
+ version "7.7.3"
+ resolved "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz"
+ integrity sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==
set-function-length@^1.2.2:
version "1.2.2"
@@ -5768,35 +6051,39 @@ set-proto@^1.0.0:
es-errors "^1.3.0"
es-object-atoms "^1.0.0"
-sharp@^0.34.1:
- version "0.34.1"
- resolved "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz"
- integrity sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==
+sharp@^0.34.3:
+ version "0.34.5"
+ resolved "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz"
+ integrity sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==
dependencies:
- color "^4.2.3"
- detect-libc "^2.0.3"
- semver "^7.7.1"
+ "@img/colour" "^1.0.0"
+ detect-libc "^2.1.2"
+ semver "^7.7.3"
optionalDependencies:
- "@img/sharp-darwin-arm64" "0.34.1"
- "@img/sharp-darwin-x64" "0.34.1"
- "@img/sharp-libvips-darwin-arm64" "1.1.0"
- "@img/sharp-libvips-darwin-x64" "1.1.0"
- "@img/sharp-libvips-linux-arm" "1.1.0"
- "@img/sharp-libvips-linux-arm64" "1.1.0"
- "@img/sharp-libvips-linux-ppc64" "1.1.0"
- "@img/sharp-libvips-linux-s390x" "1.1.0"
- "@img/sharp-libvips-linux-x64" "1.1.0"
- "@img/sharp-libvips-linuxmusl-arm64" "1.1.0"
- "@img/sharp-libvips-linuxmusl-x64" "1.1.0"
- "@img/sharp-linux-arm" "0.34.1"
- "@img/sharp-linux-arm64" "0.34.1"
- "@img/sharp-linux-s390x" "0.34.1"
- "@img/sharp-linux-x64" "0.34.1"
- "@img/sharp-linuxmusl-arm64" "0.34.1"
- "@img/sharp-linuxmusl-x64" "0.34.1"
- "@img/sharp-wasm32" "0.34.1"
- "@img/sharp-win32-ia32" "0.34.1"
- "@img/sharp-win32-x64" "0.34.1"
+ "@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"
shebang-command@^2.0.0:
version "2.0.0"
@@ -5877,11 +6164,6 @@ stable-hash@^0.0.5:
resolved "https://registry.npmjs.org/stable-hash/-/stable-hash-0.0.5.tgz"
integrity sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==
-streamsearch@^1.1.0:
- version "1.1.0"
- resolved "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz"
- integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==
-
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
@@ -6000,9 +6282,9 @@ strip-ansi@^6.0.0, strip-ansi@^6.0.1:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
- version "7.1.0"
- resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz"
- integrity sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==
+ version "7.1.2"
+ resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz"
+ integrity sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==
dependencies:
ansi-regex "^6.0.1"
@@ -6037,19 +6319,6 @@ styled-jsx@5.1.6:
dependencies:
client-only "0.0.1"
-sucrase@^3.35.0:
- version "3.35.0"
- resolved "https://registry.npmjs.org/sucrase/-/sucrase-3.35.0.tgz"
- integrity sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==
- dependencies:
- "@jridgewell/gen-mapping" "^0.3.2"
- commander "^4.0.0"
- glob "^10.3.10"
- lines-and-columns "^1.1.6"
- mz "^2.7.0"
- pirates "^4.0.1"
- ts-interface-checker "^0.1.9"
-
supercluster@^8.0.1:
version "8.0.1"
resolved "https://registry.npmjs.org/supercluster/-/supercluster-8.0.1.tgz"
@@ -6074,10 +6343,15 @@ tailwind-merge@^1.14.0:
resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-1.14.0.tgz"
integrity sha512-3mFKyCo/MBcgyOTlrY8T7odzZFx+w+qKSMAmdFzRvqBfLlSigU6TZnlFHK0lkMwj9Bj8OYU+9yW9lmGuS0QEnQ==
-tailwind-merge@^2.5.2, tailwind-merge@^2.5.4, tailwind-merge@2.5.4:
- version "2.5.4"
- resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.4.tgz"
- integrity sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==
+tailwind-merge@^2.5.2:
+ version "2.6.1"
+ resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.1.tgz"
+ integrity sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==
+
+tailwind-merge@^3.4.0, tailwind-merge@>=3.0.0, tailwind-merge@3.4.0:
+ version "3.4.0"
+ resolved "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.4.0.tgz"
+ integrity sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g==
tailwind-variants@^0.1.20:
version "0.1.20"
@@ -6086,72 +6360,38 @@ tailwind-variants@^0.1.20:
dependencies:
tailwind-merge "^1.14.0"
-tailwind-variants@0.3.0:
- version "0.3.0"
- resolved "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-0.3.0.tgz"
- integrity sha512-ho2k5kn+LB1fT5XdNS3Clb96zieWxbStE9wNLK7D0AV64kdZMaYzAKo0fWl6fXLPY99ffF9oBJnIj5escEl/8A==
- dependencies:
- tailwind-merge "^2.5.4"
+tailwind-variants@3.2.2:
+ version "3.2.2"
+ resolved "https://registry.npmjs.org/tailwind-variants/-/tailwind-variants-3.2.2.tgz"
+ integrity sha512-Mi4kHeMTLvKlM98XPnK+7HoBPmf4gygdFmqQPaDivc3DpYS6aIY6KiG/PgThrGvii5YZJqRsPz0aPyhoFzmZgg==
-tailwindcss@*, tailwindcss@^3.4.1, tailwindcss@>=3.4.0:
- version "3.4.17"
- resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz"
- integrity sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==
- dependencies:
- "@alloc/quick-lru" "^5.2.0"
- arg "^5.0.2"
- chokidar "^3.6.0"
- didyoumean "^1.2.2"
- dlv "^1.1.3"
- fast-glob "^3.3.2"
- glob-parent "^6.0.2"
- is-glob "^4.0.3"
- jiti "^1.21.6"
- lilconfig "^3.1.3"
- micromatch "^4.0.8"
- normalize-path "^3.0.0"
- object-hash "^3.0.0"
- picocolors "^1.1.1"
- postcss "^8.4.47"
- postcss-import "^15.1.0"
- postcss-js "^4.0.1"
- postcss-load-config "^4.0.2"
- postcss-nested "^6.2.0"
- postcss-selector-parser "^6.1.2"
- resolve "^1.22.8"
- sucrase "^3.35.0"
+tailwindcss@*, tailwindcss@^4.1.18, tailwindcss@>=3.4.0, tailwindcss@>=4.0.0, tailwindcss@4.1.18:
+ version "4.1.18"
+ resolved "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz"
+ integrity sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==
+
+tapable@^2.2.0:
+ version "2.3.0"
+ resolved "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz"
+ integrity sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==
text-table@^0.2.0:
version "0.2.0"
resolved "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz"
integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==
-thenify-all@^1.0.0:
- version "1.6.0"
- resolved "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz"
- integrity sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==
- dependencies:
- thenify ">= 3.1.0 < 4"
-
-"thenify@>= 3.1.0 < 4":
- version "3.3.1"
- resolved "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz"
- integrity sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==
- dependencies:
- any-promise "^1.0.0"
-
tiny-invariant@^1.3.3:
version "1.3.3"
resolved "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz"
integrity sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==
-tinyglobby@^0.2.13:
- version "0.2.13"
- resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz"
- integrity sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==
+tinyglobby@^0.2.13, tinyglobby@^0.2.15:
+ version "0.2.15"
+ resolved "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz"
+ integrity sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==
dependencies:
- fdir "^6.4.4"
- picomatch "^4.0.2"
+ fdir "^6.5.0"
+ picomatch "^4.0.3"
to-regex-range@^5.0.1:
version "5.0.1"
@@ -6170,15 +6410,10 @@ trough@^2.0.0:
resolved "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz"
integrity sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==
-ts-api-utils@^2.1.0:
- version "2.1.0"
- resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz"
- integrity sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==
-
-ts-interface-checker@^0.1.9:
- version "0.1.13"
- resolved "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz"
- integrity sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==
+ts-api-utils@^2.4.0:
+ version "2.4.0"
+ resolved "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz"
+ integrity sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==
tsconfig-paths@^3.15.0:
version "3.15.0"
@@ -6259,10 +6494,10 @@ typed-array-length@^1.0.7:
possible-typed-array-names "^1.0.0"
reflect.getprototypeof "^1.0.6"
-typescript@^5.8.3, typescript@>=3.3.1, typescript@>=4.8.4, "typescript@>=4.8.4 <5.9.0":
- version "5.8.3"
- resolved "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz"
- integrity sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==
+typescript@^5.9.3, typescript@>=3.3.1, typescript@>=4.8.4, "typescript@>=4.8.4 <6.0.0":
+ version "5.9.3"
+ resolved "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz"
+ integrity sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==
unbox-primitive@^1.1.0:
version "1.1.0"
@@ -6368,9 +6603,9 @@ use-composed-ref@^1.3.0:
integrity sha512-djviaxuOOh7wkj0paeO1Q/4wMZ8Zrnag5H6yBvzN7AKKe8beOaED9SF5/ByLqsku8NP4zQqsvM2u3ew/tJK8/w==
use-isomorphic-layout-effect@^1.1.1:
- version "1.2.0"
- resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.0.tgz"
- integrity sha512-q6ayo8DWoPZT0VdG4u3D3uxcgONP3Mevx2i2b0434cwWBoL+aelL1DzkXI6w3PhTZzUeR2kaVlZn70iCiseP6w==
+ version "1.2.1"
+ resolved "https://registry.npmjs.org/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.2.1.tgz"
+ integrity sha512-tpZZ+EX0gaghDAiFR37hj5MgY6ZN55kLiPkJsKxBMZ6GZdOSPJXiOzPM984oPYZ5AnehYx5WQp1+ME8I/P/pRA==
use-latest@^1.2.1:
version "1.3.0"
@@ -6384,11 +6619,6 @@ use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0:
resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz"
integrity sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==
-util-deprecate@^1.0.2:
- version "1.0.2"
- resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
- integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
-
vfile-message@^4.0.0:
version "4.0.3"
resolved "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.3.tgz"
@@ -6508,16 +6738,6 @@ wrap-ansi@^8.1.0:
string-width "^5.0.1"
strip-ansi "^7.0.1"
-wrappy@1:
- version "1.0.2"
- resolved "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
- integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
-
-yaml@^2.3.4:
- version "2.7.1"
- resolved "https://registry.npmjs.org/yaml/-/yaml-2.7.1.tgz"
- integrity sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==
-
yocto-queue@^0.1.0:
version "0.1.0"
resolved "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz"