diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..72e9aa42 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,7 @@ +Dockerfile +.dockerignore +node_modules +npm-debug.log +README.md +.next +.git \ No newline at end of file diff --git a/.env.development b/.env.development new file mode 100644 index 00000000..1335bf8c --- /dev/null +++ b/.env.development @@ -0,0 +1,4 @@ +NEXT_PUBLIC_API_URL=https://dev.say-cheese.me +NEXT_PUBLIC_KAKAO_AUTH_URL=https://dev.say-cheese.me/oauth2/authorization/kakao +NEXT_PUBLIC_CLIENT_URL=http://localhost:3000 +NEXT_PUBLIC_KAKAO_JS_KEY=06fe9be27d60a133d4b9d25b9628f274 diff --git a/.env.production b/.env.production new file mode 100644 index 00000000..bc107e0c --- /dev/null +++ b/.env.production @@ -0,0 +1,5 @@ +NEXT_PUBLIC_API_URL=https://dev.say-cheese.me +# NEXT_PUBLIC_API_URL=https://api.say-cheese.me +NEXT_PUBLIC_KAKAO_AUTH_URL=https://dev.say-cheese.me/oauth2/authorization/kakao +NEXT_PUBLIC_CLIENT_URL=https://say-cheese.me +NEXT_PUBLIC_KAKAO_JS_KEY=06fe9be27d60a133d4b9d25b9628f274 diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..a131ed24 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,20 @@ +--- +name: Frontend Issue +about: 프론트엔드 관련 이슈 및 개발 요청 (UI/UX, 컴포넌트, 페이지 등) +title: '' +labels: '' +assignees: '' +--- + +## 🎯 이슈 유형 + +- [ ] 🐛 버그 수정 +- [ ] ✨ 새 기능 개발 +- [ ] 🎨 UI/UX 개선 +- [ ] 📱 반응형 대응 +- [ ] ⚡ 성능 최적화 +- [ ] 🔧 리팩토링 + +## 📝 상세 설명 + + diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..7cb246ff --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,36 @@ +## 요약 + + + +## 구현 사항 + + + +- [ ] +- [ ] +- [ ] +- [ ] + +## 📸 스크린샷 + + + +## Need Review + +- ~ 부분 이렇게 구현했어요, 피드백 부탁해요! + + +## Reference + + + +### 📜 리뷰 규칙 + +Reviewer는 아래 **P5 Rule**을 참고하여 리뷰를 진행합니다. +P5 Rule을 통해 Reviewer는 Reviewee에게 리뷰의 의도를 보다 정확히 전달할 수 있습니다. + +- P1: 꼭 반영해주세요 (Comment) +- P2: 적극적으로 고려해주세요 (Comment) +- P3: 웬만하면 반영해 주세요 (Comment) +- P4: 반영해도 좋고 넘어가도 좋습니다 (Approve) +- P5: 그냥 사소한 의견입니다 (Approve) diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 00000000..aac174f9 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,59 @@ +name: deploy + +# on: +# push: +# branches: [main] + +jobs: + app-build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - uses: docker/setup-buildx-action@v3 + + - uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_TOKEN }} + + - name: Build & Push + uses: docker/build-push-action@v6 + with: + context: . + file: ./Dockerfile + push: true + platforms: linux/amd64 + tags: | + ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGENAME }}:${{ github.run_id }} + ${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGENAME }}:latest + cache-from: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGENAME }}:buildcache + cache-to: type=registry,ref=${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGENAME }}:buildcache,mode=max + + - name: Deploy via SSH + uses: appleboy/ssh-action@v1.2.0 + with: + host: ${{ secrets.SSH_HOST }} + username: ${{ secrets.SSH_USERNAME }} + password: ${{ secrets.SSH_PASSWORD }} + port: ${{ secrets.SSH_PORT }} + script: | + set -e + APP=${{ secrets.DOCKER_IMAGENAME }} + IMAGE=${{ secrets.DOCKER_USERNAME }}/${{ secrets.DOCKER_IMAGENAME }}:${{ github.run_id }} + + docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_TOKEN }} + docker pull $IMAGE + + # 기존 컨테이너 종료/삭제 + docker stop ${APP} || true + docker rm ${APP} || true + + # 새 컨테이너(임시 이름) 먼저 띄우고 헬스체크 + docker run -d --name ${APP} \ + --restart=always \ + -p 3000:3000 \ + $IMAGE + + # 오래된 이미지 정리(선택) + docker image prune -f || true diff --git a/.github/workflows/lint-checker.yml b/.github/workflows/lint-checker.yml new file mode 100644 index 00000000..d7b81c06 --- /dev/null +++ b/.github/workflows/lint-checker.yml @@ -0,0 +1,26 @@ +name: Lint Checker + +on: + pull_request: + types: [opened, synchronize, reopened] + +jobs: + lint: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + with: + run_install: true + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Run lint + run: pnpm lint diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..c57681fe --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,29 @@ +name: CI - Test + +on: + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + run_install: false + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'pnpm' + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Run tests + run: pnpm test diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..5340dd9a --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 00000000..4b7ffd9c --- /dev/null +++ b/.prettierignore @@ -0,0 +1,4 @@ +*.yaml +*.css +**/token.json +src/global/api/ep.ts diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..91cc9e17 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,10 @@ +{ + "semi": true, + "singleQuote": true, + "jsxSingleQuote": true, + "trailingComma": "all", + "tabWidth": 2, + "printWidth": 80, + "endOfLine": "lf", + "plugins": ["prettier-plugin-tailwindcss"] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..0b1c1e74 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,6 @@ +{ + "recommendations": [ + "esbenp.prettier-vscode", // Prettier - 코드 포매터 + "bradlc.vscode-tailwindcss" // Tailwind CSS IntelliSense + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..201cb3f8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "files.eol": "\n", + "files.insertFinalNewline": true, + "files.trimTrailingWhitespace": true, + "prettier.endOfLine": "lf", + "editor.formatOnSave": true, + "editor.codeActionsOnSave": { + "source.organizeImports": "explicit", + "source.fixAll.eslint": "explicit" + } +} diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..02e5f051 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,66 @@ +# syntax=docker.io/docker/dockerfile:1 + +FROM node:22-alpine AS base + +# Install dependencies only when needed +FROM base AS deps +# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed. +RUN apk add --no-cache libc6-compat +WORKDIR /app + +# Install dependencies based on the preferred package manager +COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* .npmrc* ./ +RUN \ + if [ -f yarn.lock ]; then yarn --frozen-lockfile; \ + elif [ -f package-lock.json ]; then npm ci; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm i --frozen-lockfile; \ + else echo "Lockfile not found." && exit 1; \ + fi + + +# Rebuild the source code only when needed +FROM base AS builder +WORKDIR /app +COPY --from=deps /app/node_modules ./node_modules +COPY . . + +# Next.js collects completely anonymous telemetry data about general usage. +# Learn more here: https://nextjs.org/telemetry +# Uncomment the following line in case you want to disable telemetry during the build. +# ENV NEXT_TELEMETRY_DISABLED=1 + +RUN \ + if [ -f yarn.lock ]; then yarn run build; \ + elif [ -f package-lock.json ]; then npm run build; \ + elif [ -f pnpm-lock.yaml ]; then corepack enable pnpm && pnpm run build; \ + else echo "Lockfile not found." && exit 1; \ + fi + +# Production image, copy all the files and run next +FROM base AS runner +WORKDIR /app + +ENV NODE_ENV=production +# Uncomment the following line in case you want to disable telemetry during runtime. +# ENV NEXT_TELEMETRY_DISABLED=1 + +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +COPY --from=builder /app/public ./public + +# Automatically leverage output traces to reduce image size +# https://nextjs.org/docs/advanced-features/output-file-tracing +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +USER nextjs + +EXPOSE 3000 + +ENV PORT=3000 + +# server.js is created by next build from the standalone output +# https://nextjs.org/docs/pages/api-reference/config/next-config-js/output +ENV HOSTNAME="0.0.0.0" +CMD ["node", "server.js"] \ No newline at end of file diff --git a/README.md b/README.md index 99291317..e4d30f92 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,95 @@ -# FE -🧀 Kusitms 32nd Cheeeese Frontend Repository 🧀 +# 🧀 치이이즈 : 딱 7일만 열리는 특별한 공유 앨범 서비스 + +> 🔗 서비스 링크: [https://say-cheese.me](https://say-cheese.me) + +![웹 썸네일](https://github.com/user-attachments/assets/f5a6c97a-21b9-4dff-a7b7-8c12fe6e27db) + +## 🧑‍🤝‍🧑 Frontend Members + +| **김규태** | **김건우** | +| :-----------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------: | +| | | +| `frontend` | `frontend` | + +## 🏛️ System Architecture + +image + +## 🛠️ 기술 스택 + +- Language & Framework + - Next.js 15 + - React + - TypeScript + +- State & Data Management + - Zustand + - TanStack Query + +- UI Utilities + - Tailwind CSS + - shadcn/ui + - Framer Motion + - Lucide Icons + +- Testing + - Vitest + +- CI/CD + - GitHub Actions + +## 📂 폴더 구조 + +```csharp +src +├── app # 🌐 Next.js App Router (페이지 엔트리) +│ +├── components +│ └── ui # 🎨 공통 UI 컴포넌트 (shadcn 기반) +│ +├── feature # 📦 도메인 단위 기능 모듈 (components, hooks, utils, svg, constants 폴더로 구성) +│ ├── album # 📸 앨범 도메인 +│ │ ├── 4cut # 🎞️ 치즈네컷 생성 / 렌더링 +│ │ ├── detail # 🖼️ 앨범 상세 화면 +│ │ └── qrcode # 🔳 앨범 QR 코드 생성 +│ ├── album-entry # 🚪 앨범 입장 플로우 +│ ├── album-select # 📂 앨범 선택 화면 +│ ├── create-album # 📝 앨범 생성 +│ ├── login # 🔐 로그인 / 인증 플로우 +│ ├── main # 🏠 메인 화면 +│ ├── mypage # 🙋‍♂️ 마이페이지 +│ ├── onboarding # 🎉 온보딩 +│ ├── photo-detail # 🖼️ 사진 상세 +│ ├── photo-entry # 📥 사진 업로드 입장 +│ ├── root # 🌳 랜딩페이지 +│ ├── term # 📄 약관 +│ └── upload # ⬆️ 사진 업로드 +│ +├── global # 🌍 전역 설정 / 공통 모듈 +│ ├── api # 🔗 API 클라이언트 / 엔드포인트 +│ ├── components # 🧩 글로벌 공통 컴포넌트 +│ ├── constants # 🧱 상수 +│ ├── context # ⚛️ 컨텍스트 프로바이더 +│ ├── hooks # 🪝 공용 Hooks +│ ├── svg # 🖼️ SVG 에셋 +│ ├── types # 🧾 타입 정의 +│ └── utils # 🔧 공용 유틸리티 함수 +│ +└── store # 🗂️ Zustand 스토어 + +``` + +## 💬 Commit Convention + +### {이슈Type} : 커밋내용 + +ex. feat : 앨범나가기 버튼추가 + +| Type | 내용 | +| ---------- | ----------------------------------- | +| `feat` | 새로운 기능 구현 | +| `chore` | 부수적인 코드 수정 및 기타 변경사항 | +| `docs` | 문서 추가 및 수정, 삭제 | +| `fix` | 버그 수정 | +| `test` | 테스트 코드 추가 및 수정, 삭제 | +| `refactor` | 코드 리팩토링 | diff --git a/components.json b/components.json new file mode 100644 index 00000000..edcaef26 --- /dev/null +++ b/components.json @@ -0,0 +1,22 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "src/app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "iconLibrary": "lucide", + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "registries": {} +} diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 00000000..06dcd91b --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,35 @@ +import { FlatCompat } from '@eslint/eslintrc'; +import { dirname } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const compat = new FlatCompat({ + baseDirectory: __dirname, +}); + +const eslintConfig = [ + ...compat.extends( + 'next/core-web-vitals', + 'next/typescript', + 'plugin:prettier/recommended', + ), + { + ignores: [ + 'node_modules/**', + '.next/**', + 'out/**', + 'build/**', + 'next-env.d.ts', + 'scripts/**', + 'src/global/api/ep.ts', + ], + rules: { + '@typescript-eslint/no-empty-object-type': 'off', // 빈 타입 객체 허용 + '@next/next/no-img-element': 'off', + }, + }, +]; + +export default eslintConfig; diff --git a/middleware.ts b/middleware.ts new file mode 100644 index 00000000..dd971821 --- /dev/null +++ b/middleware.ts @@ -0,0 +1,13 @@ +import type { NextRequest } from 'next/server'; +import { NextResponse } from 'next/server'; + +export function middleware(req: NextRequest) { + const res = NextResponse.next(); + + // HTML 요청일 때만 붙임 + if (req.nextUrl.pathname === '/') { + res.headers.set('Cache-Control', 'public, max-age=86400'); + } + + return res; +} diff --git a/next.config.ts b/next.config.ts new file mode 100644 index 00000000..40a41346 --- /dev/null +++ b/next.config.ts @@ -0,0 +1,11 @@ +import type { NextConfig } from 'next'; + +const nextConfig: NextConfig = { + output: 'standalone', + images: { + domains: ['say-cheese-profile.edge.naverncp.com'], + deviceSizes: [320, 480, 590, 640, 750, 828, 1080, 1200, 1920], + }, +}; + +export default nextConfig; diff --git a/package.json b/package.json new file mode 100644 index 00000000..ab099f01 --- /dev/null +++ b/package.json @@ -0,0 +1,67 @@ +{ + "name": "cheeese_fe", + "version": "0.1.0", + "packageManager": "pnpm@9.12.3", + "engines": { + "node": "22.20.0" + }, + "private": true, + "scripts": { + "dev": "next dev --turbopack", + "build": "next build --turbopack", + "build:docker": "docker build -t cheese_frontend .", + "start": "next start", + "lint": "eslint", + "test": "vitest", + "token": "node scripts/generate-color-token.mjs", + "ep": "node scripts/generate-endpoint.ts" + }, + "dependencies": { + "@headlessui/react": "^2.2.9", + "@radix-ui/react-alert-dialog": "^1.1.15", + "@radix-ui/react-dialog": "^1.1.15", + "@radix-ui/react-popover": "^1.1.15", + "@radix-ui/react-slot": "^1.2.3", + "@tanstack/react-query": "^5.90.2", + "axios": "^1.12.2", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "date-fns": "^4.1.0", + "embla-carousel-react": "^8.6.0", + "emoji-picker-react": "^4.14.2", + "exifr": "^7.1.3", + "framer-motion": "^12.23.24", + "heic-to": "^1.3.0", + "html-to-image": "^1.11.13", + "jszip": "^3.10.1", + "lottie-react": "^2.4.1", + "lucide-react": "^0.546.0", + "next": "15.5.7", + "react": "19.1.2", + "react-day-picker": "^9.11.1", + "react-dom": "19.1.2", + "react-qr-code": "^2.0.18", + "swiper": "^12.0.3", + "tailwind-merge": "^3.3.1", + "use-sync-external-store": "^1.6.0", + "vaul": "^1.1.2", + "zustand": "^5.0.8" + }, + "devDependencies": { + "@eslint/eslintrc": "^3", + "@tailwindcss/postcss": "^4", + "@types/node": "^20", + "@types/react": "^19", + "@types/react-dom": "^19", + "eslint": "^9", + "eslint-config-next": "15.5.4", + "eslint-config-prettier": "^10.1.8", + "eslint-plugin-prettier": "^5.5.4", + "prettier": "^3.6.2", + "prettier-plugin-tailwindcss": "^0.7.1", + "tailwindcss": "^4", + "tw-animate-css": "^1.4.0", + "typescript": "^5", + "vitest": "^4.0.14" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 00000000..8b73bdfb --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,5857 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@headlessui/react': + specifier: ^2.2.9 + version: 2.2.9(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-alert-dialog': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-dialog': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-popover': + specifier: ^1.1.15 + version: 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-slot': + specifier: ^1.2.3 + version: 1.2.3(@types/react@19.2.2)(react@19.1.2) + '@tanstack/react-query': + specifier: ^5.90.2 + version: 5.90.5(react@19.1.2) + axios: + specifier: ^1.12.2 + version: 1.12.2 + class-variance-authority: + specifier: ^0.7.1 + version: 0.7.1 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + date-fns: + specifier: ^4.1.0 + version: 4.1.0 + embla-carousel-react: + specifier: ^8.6.0 + version: 8.6.0(react@19.1.2) + emoji-picker-react: + specifier: ^4.14.2 + version: 4.14.2(react@19.1.2) + exifr: + specifier: ^7.1.3 + version: 7.1.3 + framer-motion: + specifier: ^12.23.24 + version: 12.23.24(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + heic-to: + specifier: ^1.3.0 + version: 1.3.0 + html-to-image: + specifier: ^1.11.13 + version: 1.11.13 + jszip: + specifier: ^3.10.1 + version: 3.10.1 + lottie-react: + specifier: ^2.4.1 + version: 2.4.1(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + lucide-react: + specifier: ^0.546.0 + version: 0.546.0(react@19.1.2) + next: + specifier: 15.5.7 + version: 15.5.7(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + react: + specifier: 19.1.2 + version: 19.1.2 + react-day-picker: + specifier: ^9.11.1 + version: 9.11.1(react@19.1.2) + react-dom: + specifier: 19.1.2 + version: 19.1.2(react@19.1.2) + react-qr-code: + specifier: ^2.0.18 + version: 2.0.18(react@19.1.2) + swiper: + specifier: ^12.0.3 + version: 12.0.3 + tailwind-merge: + specifier: ^3.3.1 + version: 3.3.1 + use-sync-external-store: + specifier: ^1.6.0 + version: 1.6.0(react@19.1.2) + vaul: + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + zustand: + specifier: ^5.0.8 + version: 5.0.8(@types/react@19.2.2)(react@19.1.2)(use-sync-external-store@1.6.0(react@19.1.2)) + devDependencies: + '@eslint/eslintrc': + specifier: ^3 + version: 3.3.1 + '@tailwindcss/postcss': + specifier: ^4 + version: 4.1.16 + '@types/node': + specifier: ^20 + version: 20.19.23 + '@types/react': + specifier: ^19 + version: 19.2.2 + '@types/react-dom': + specifier: ^19 + version: 19.2.2(@types/react@19.2.2) + eslint: + specifier: ^9 + version: 9.38.0(jiti@2.6.1) + eslint-config-next: + specifier: 15.5.4 + version: 15.5.4(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint-config-prettier: + specifier: ^10.1.8 + version: 10.1.8(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-prettier: + specifier: ^5.5.4 + version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1))(prettier@3.6.2) + prettier: + specifier: ^3.6.2 + version: 3.6.2 + prettier-plugin-tailwindcss: + specifier: ^0.7.1 + version: 0.7.1(prettier@3.6.2) + tailwindcss: + specifier: ^4 + version: 4.1.16 + tw-animate-css: + specifier: ^1.4.0 + version: 1.4.0 + typescript: + specifier: ^5 + version: 5.9.3 + vitest: + specifier: ^4.0.14 + version: 4.0.14(@types/node@20.19.23)(jiti@2.6.1)(lightningcss@1.30.2) + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@date-fns/tz@1.4.1': + resolution: {integrity: sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==} + + '@emnapi/core@1.6.0': + resolution: {integrity: sha512-zq/ay+9fNIJJtJiZxdTnXS20PllcYMX3OE23ESc4HK/bdYu3cOWYVhsOhVnXALfU/uqJIxn5NBPd9z4v+SfoSg==} + + '@emnapi/runtime@1.6.0': + resolution: {integrity: sha512-obtUmAHTMjll499P+D9A3axeJFlhdjOWdKUNs/U6QIGT7V5RjcUW1xToAzjvmgTSQhDbYn/NwfTRoJcQ2rNBxA==} + + '@emnapi/wasi-threads@1.1.0': + resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.9.0': + resolution: {integrity: sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.2': + resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.21.1': + resolution: {integrity: sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.4.1': + resolution: {integrity: sha512-csZAzkNhsgwb0I/UAV6/RGFTbiakPCf0ZrGmrIxQpYvGZ00PhTkSnyKNolphgIvmnJeGw6rcGVEXfTzUnFuEvw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.16.0': + resolution: {integrity: sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.38.0': + resolution: {integrity: sha512-UZ1VpFvXf9J06YG9xQBdnzU+kthors6KjhMAl6f4gH4usHyh31rUf2DLGInT8RFYIReYXNSydgPY0V2LuWgl7A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.7': + resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.4.0': + resolution: {integrity: sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@floating-ui/core@1.7.3': + resolution: {integrity: sha512-sGnvb5dmrJaKEZ+LDIpguvdX3bDlEllmv4/ClQ9awcmCZrlx5jQyyMWFM5kBI+EyNOCDDiKk8il0zeuX3Zlg/w==} + + '@floating-ui/dom@1.7.4': + resolution: {integrity: sha512-OOchDgh4F2CchOX94cRVqhvy7b3AFb+/rQXyswmzmGakRfkMgoWVjfnLWkRirfLEfuD4ysVW16eXzwt3jHIzKA==} + + '@floating-ui/react-dom@2.1.6': + resolution: {integrity: sha512-4JX6rEatQEvlmgU80wZyq9RT96HZJa88q8hp0pBd+LrczeDI4o6uA2M+uvxngVHo4Ihr8uibXxH6+70zhAFrVw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/react@0.26.28': + resolution: {integrity: sha512-yORQuuAtVpiRjpMhdc0wJj06b9JFjrYF4qp96j++v2NBpbi6SEGF7donUJ3TMieerQ6qVkAv1tgr7L4r5roTqw==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.10': + resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==} + + '@headlessui/react@2.2.9': + resolution: {integrity: sha512-Mb+Un58gwBn0/yWZfyrCh0TJyurtT+dETj7YHleylHk5od3dv2XqETPGWMyQ5/7sYN7oWdyM1u9MvC0OC8UmzQ==} + engines: {node: '>=10'} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.7': + resolution: {integrity: sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/colour@1.0.0': + resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.34.4': + resolution: {integrity: sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.34.4': + resolution: {integrity: sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.2.3': + resolution: {integrity: sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.2.3': + resolution: {integrity: sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.2.3': + resolution: {integrity: sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.2.3': + resolution: {integrity: sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.2.3': + resolution: {integrity: sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.2.3': + resolution: {integrity: sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.2.3': + resolution: {integrity: sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + resolution: {integrity: sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + resolution: {integrity: sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.34.4': + resolution: {integrity: sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.34.4': + resolution: {integrity: sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.34.4': + resolution: {integrity: sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-s390x@0.34.4': + resolution: {integrity: sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.34.4': + resolution: {integrity: sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.34.4': + resolution: {integrity: sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.34.4': + resolution: {integrity: sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.34.4': + resolution: {integrity: sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.34.4': + resolution: {integrity: sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.34.4': + resolution: {integrity: sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.34.4': + resolution: {integrity: sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/wasm-runtime@0.2.12': + resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + + '@next/env@15.5.7': + resolution: {integrity: sha512-4h6Y2NyEkIEN7Z8YxkA27pq6zTkS09bUSYC0xjd0NpwFxjnIKeZEeH591o5WECSmjpUhLn3H2QLJcDye3Uzcvg==} + + '@next/eslint-plugin-next@15.5.4': + resolution: {integrity: sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==} + + '@next/swc-darwin-arm64@15.5.7': + resolution: {integrity: sha512-IZwtxCEpI91HVU/rAUOOobWSZv4P2DeTtNaCdHqLcTJU4wdNXgAySvKa/qJCgR5m6KI8UsKDXtO2B31jcaw1Yw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@next/swc-darwin-x64@15.5.7': + resolution: {integrity: sha512-UP6CaDBcqaCBuiq/gfCEJw7sPEoX1aIjZHnBWN9v9qYHQdMKvCKcAVs4OX1vIjeE+tC5EIuwDTVIoXpUes29lg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@next/swc-linux-arm64-gnu@15.5.7': + resolution: {integrity: sha512-NCslw3GrNIw7OgmRBxHtdWFQYhexoUCq+0oS2ccjyYLtcn1SzGzeM54jpTFonIMUjNbHmpKpziXnpxhSWLcmBA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-arm64-musl@15.5.7': + resolution: {integrity: sha512-nfymt+SE5cvtTrG9u1wdoxBr9bVB7mtKTcj0ltRn6gkP/2Nu1zM5ei8rwP9qKQP0Y//umK+TtkKgNtfboBxRrw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@next/swc-linux-x64-gnu@15.5.7': + resolution: {integrity: sha512-hvXcZvCaaEbCZcVzcY7E1uXN9xWZfFvkNHwbe/n4OkRhFWrs1J1QV+4U1BN06tXLdaS4DazEGXwgqnu/VMcmqw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-linux-x64-musl@15.5.7': + resolution: {integrity: sha512-4IUO539b8FmF0odY6/SqANJdgwn1xs1GkPO5doZugwZ3ETF6JUdckk7RGmsfSf7ws8Qb2YB5It33mvNL/0acqA==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@next/swc-win32-arm64-msvc@15.5.7': + resolution: {integrity: sha512-CpJVTkYI3ZajQkC5vajM7/ApKJUOlm6uP4BknM3XKvJ7VXAvCqSjSLmM0LKdYzn6nBJVSjdclx8nYJSa3xlTgQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@next/swc-win32-x64-msvc@15.5.7': + resolution: {integrity: sha512-gMzgBX164I6DN+9/PGA+9dQiwmTkE4TloBNx8Kv9UiGARsr9Nba7IpcBRA1iTV9vwlYnrE3Uy6I7Aj6qLjQuqw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@nolyfill/is-core-module@1.0.39': + resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} + engines: {node: '>=12.4.0'} + + '@pkgr/core@0.2.9': + resolution: {integrity: sha512-QNqXyfVS2wm9hweSYD2O7F0G06uurj9kZ96TRQE5Y9hU7+tgdZwIkbAKc5Ocy1HxEY2kuDQa6cQ1WRs/O5LFKA==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + + '@radix-ui/primitive@1.1.3': + resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + + '@radix-ui/react-alert-dialog@1.1.15': + resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.2': + resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.15': + resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.11': + resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.3': + resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-popover@1.1.15': + resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.8': + resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.5': + resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.2.3': + resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + + '@react-aria/focus@3.21.2': + resolution: {integrity: sha512-JWaCR7wJVggj+ldmM/cb/DXFg47CXR55lznJhZBh4XVqJjMKwaOOqpT5vNN7kpC1wUpXicGNuDnJDN1S/+6dhQ==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/interactions@3.25.6': + resolution: {integrity: sha512-5UgwZmohpixwNMVkMvn9K1ceJe6TzlRlAfuYoQDUuOkk62/JVJNDLAPKIf5YMRc7d2B0rmfgaZLMtbREb0Zvkw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/ssr@3.9.10': + resolution: {integrity: sha512-hvTm77Pf+pMBhuBm760Li0BVIO38jv1IBws1xFm1NoL26PU+fe+FMW5+VZWyANR6nYL65joaJKZqOdTQMkO9IQ==} + engines: {node: '>= 12'} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-aria/utils@3.31.0': + resolution: {integrity: sha512-ABOzCsZrWzf78ysswmguJbx3McQUja7yeGj6/vZo4JVsZNlxAN+E9rs381ExBRI0KzVo6iBTeX5De8eMZPJXig==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + react-dom: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-stately/flags@3.1.2': + resolution: {integrity: sha512-2HjFcZx1MyQXoPqcBGALwWWmgFVUk2TuKVIQxCbRq7fPyWXIl6VHcakCLurdtYC2Iks7zizvz0Idv48MQ38DWg==} + + '@react-stately/utils@3.10.8': + resolution: {integrity: sha512-SN3/h7SzRsusVQjQ4v10LaVsDc81jyyR0DD5HnsQitm/I5WDpaSr2nRHtyloPFU48jlql1XX/S04T2DLQM7Y3g==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@react-types/shared@3.32.1': + resolution: {integrity: sha512-famxyD5emrGGpFuUlgOP6fVW2h/ZaF405G5KDi3zPHzyjAWys/8W6NAVJtNbkCkhedmvL0xOhvt8feGXyXaw5w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0 || ^19.0.0-rc.1 + + '@rollup/rollup-android-arm-eabi@4.53.3': + resolution: {integrity: sha512-mRSi+4cBjrRLoaal2PnqH82Wqyb+d3HsPUN/W+WslCXsZsyHa9ZeQQX/pQsZaVIWDkPcpV6jJ+3KLbTbgnwv8w==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.53.3': + resolution: {integrity: sha512-CbDGaMpdE9sh7sCmTrTUyllhrg65t6SwhjlMJsLr+J8YjFuPmCEjbBSx4Z/e4SmDyH3aB5hGaJUP2ltV/vcs4w==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.53.3': + resolution: {integrity: sha512-Nr7SlQeqIBpOV6BHHGZgYBuSdanCXuw09hon14MGOLGmXAFYjx1wNvquVPmpZnl0tLjg25dEdr4IQ6GgyToCUA==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.53.3': + resolution: {integrity: sha512-DZ8N4CSNfl965CmPktJ8oBnfYr3F8dTTNBQkRlffnUarJ2ohudQD17sZBa097J8xhQ26AwhHJ5mvUyQW8ddTsQ==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.53.3': + resolution: {integrity: sha512-yMTrCrK92aGyi7GuDNtGn2sNW+Gdb4vErx4t3Gv/Tr+1zRb8ax4z8GWVRfr3Jw8zJWvpGHNpss3vVlbF58DZ4w==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.53.3': + resolution: {integrity: sha512-lMfF8X7QhdQzseM6XaX0vbno2m3hlyZFhwcndRMw8fbAGUGL3WFMBdK0hbUBIUYcEcMhVLr1SIamDeuLBnXS+Q==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + resolution: {integrity: sha512-k9oD15soC/Ln6d2Wv/JOFPzZXIAIFLp6B+i14KhxAfnq76ajt0EhYc5YPeX6W1xJkAdItcVT+JhKl1QZh44/qw==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + resolution: {integrity: sha512-vTNlKq+N6CK/8UktsrFuc+/7NlEYVxgaEgRXVUVK258Z5ymho29skzW1sutgYjqNnquGwVUObAaxae8rZ6YMhg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + resolution: {integrity: sha512-RGrFLWgMhSxRs/EWJMIFM1O5Mzuz3Xy3/mnxJp/5cVhZ2XoCAxJnmNsEyeMJtpK+wu0FJFWz+QF4mjCA7AUQ3w==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.53.3': + resolution: {integrity: sha512-kASyvfBEWYPEwe0Qv4nfu6pNkITLTb32p4yTgzFCocHnJLAHs+9LjUu9ONIhvfT/5lv4YS5muBHyuV84epBo/A==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + resolution: {integrity: sha512-JiuKcp2teLJwQ7vkJ95EwESWkNRFJD7TQgYmCnrPtlu50b4XvT5MOmurWNrCj3IFdyjBQ5p9vnrX4JM6I8OE7g==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + resolution: {integrity: sha512-EoGSa8nd6d3T7zLuqdojxC20oBfNT8nexBbB/rkxgKj5T5vhpAQKKnD+h3UkoMuTyXkP5jTjK/ccNRmQrPNDuw==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + resolution: {integrity: sha512-4s+Wped2IHXHPnAEbIB0YWBv7SDohqxobiiPA1FIWZpX+w9o2i4LezzH/NkFUl8LRci/8udci6cLq+jJQlh+0g==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + resolution: {integrity: sha512-68k2g7+0vs2u9CxDt5ktXTngsxOQkSEV/xBbwlqYcUrAVh6P9EgMZvFsnHy4SEiUl46Xf0IObWVbMvPrr2gw8A==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + resolution: {integrity: sha512-VYsFMpULAz87ZW6BVYw3I6sWesGpsP9OPcyKe8ofdg9LHxSbRMd7zrVrr5xi/3kMZtpWL/wC+UIJWJYVX5uTKg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.53.3': + resolution: {integrity: sha512-3EhFi1FU6YL8HTUJZ51imGJWEX//ajQPfqWLI3BQq4TlvHy4X0MOr5q3D2Zof/ka0d5FNdPwZXm3Yyib/UEd+w==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.53.3': + resolution: {integrity: sha512-eoROhjcc6HbZCJr+tvVT8X4fW3/5g/WkGvvmwz/88sDtSJzO7r/blvoBDgISDiCjDRZmHpwud7h+6Q9JxFwq1Q==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openharmony-arm64@4.53.3': + resolution: {integrity: sha512-OueLAWgrNSPGAdUdIjSWXw+u/02BRTcnfw9PN41D2vq/JSEPnJnVuBgw18VkN8wcd4fjUs+jFHVM4t9+kBSNLw==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + resolution: {integrity: sha512-GOFuKpsxR/whszbF/bzydebLiXIHSgsEUp6M0JI8dWvi+fFa1TD6YQa4aSZHtpmh2/uAlj/Dy+nmby3TJ3pkTw==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + resolution: {integrity: sha512-iah+THLcBJdpfZ1TstDFbKNznlzoxa8fmnFYK4V67HvmuNYkVdAywJSoteUszvBQ9/HqN2+9AZghbajMsFT+oA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.53.3': + resolution: {integrity: sha512-J9QDiOIZlZLdcot5NXEepDkstocktoVjkaKUtqzgzpt2yWjGlbYiKyp05rWwk4nypbYUNoFAztEgixoLaSETkg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.53.3': + resolution: {integrity: sha512-UhTd8u31dXadv0MopwGgNOBpUVROFKWVQgAg5N1ESyCz8AuBcMqm4AuTjrwgQKGDfoFuz02EuMRHQIw/frmYKQ==} + cpu: [x64] + os: [win32] + + '@rtsao/scc@1.1.0': + resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==} + + '@rushstack/eslint-patch@1.14.1': + resolution: {integrity: sha512-jGTk8UD/RdjsNZW8qq10r0RBvxL8OWtoT+kImlzPDFilmozzM+9QmIJsmze9UiSBrFU45ZxhTYBypn9q9z/VfQ==} + + '@standard-schema/spec@1.0.0': + resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + + '@swc/helpers@0.5.15': + resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} + + '@tailwindcss/node@4.1.16': + resolution: {integrity: sha512-BX5iaSsloNuvKNHRN3k2RcCuTEgASTo77mofW0vmeHkfrDWaoFAFvNHpEgtu0eqyypcyiBkDWzSMxJhp3AUVcw==} + + '@tailwindcss/oxide-android-arm64@4.1.16': + resolution: {integrity: sha512-8+ctzkjHgwDJ5caq9IqRSgsP70xhdhJvm+oueS/yhD5ixLhqTw9fSL1OurzMUhBwE5zK26FXLCz2f/RtkISqHA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.1.16': + resolution: {integrity: sha512-C3oZy5042v2FOALBZtY0JTDnGNdS6w7DxL/odvSny17ORUnaRKhyTse8xYi3yKGyfnTUOdavRCdmc8QqJYwFKA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.1.16': + resolution: {integrity: sha512-vjrl/1Ub9+JwU6BP0emgipGjowzYZMjbWCDqwA2Z4vCa+HBSpP4v6U2ddejcHsolsYxwL5r4bPNoamlV0xDdLg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.1.16': + resolution: {integrity: sha512-TSMpPYpQLm+aR1wW5rKuUuEruc/oOX3C7H0BTnPDn7W/eMw8W+MRMpiypKMkXZfwH8wqPIRKppuZoedTtNj2tg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16': + resolution: {integrity: sha512-p0GGfRg/w0sdsFKBjMYvvKIiKy/LNWLWgV/plR4lUgrsxFAoQBFrXkZ4C0w8IOXfslB9vHK/JGASWD2IefIpvw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.16': + resolution: {integrity: sha512-DoixyMmTNO19rwRPdqviTrG1rYzpxgyYJl8RgQvdAQUzxC1ToLRqtNJpU/ATURSKgIg6uerPw2feW0aS8SNr/w==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.1.16': + resolution: {integrity: sha512-H81UXMa9hJhWhaAUca6bU2wm5RRFpuHImrwXBUvPbYb+3jo32I9VIwpOX6hms0fPmA6f2pGVlybO6qU8pF4fzQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.1.16': + resolution: {integrity: sha512-ZGHQxDtFC2/ruo7t99Qo2TTIvOERULPl5l0K1g0oK6b5PGqjYMga+FcY1wIUnrUxY56h28FxybtDEla+ICOyew==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.1.16': + resolution: {integrity: sha512-Oi1tAaa0rcKf1Og9MzKeINZzMLPbhxvm7rno5/zuP1WYmpiG0bEHq4AcRUiG2165/WUzvxkW4XDYCscZWbTLZw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.1.16': + resolution: {integrity: sha512-B01u/b8LteGRwucIBmCQ07FVXLzImWESAIMcUU6nvFt/tYsQ6IHz8DmZ5KtvmwxD+iTYBtM1xwoGXswnlu9v0Q==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.16': + resolution: {integrity: sha512-zX+Q8sSkGj6HKRTMJXuPvOcP8XfYON24zJBRPlszcH1Np7xuHXhWn8qfFjIujVzvH3BHU+16jBXwgpl20i+v9A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.1.16': + resolution: {integrity: sha512-m5dDFJUEejbFqP+UXVstd4W/wnxA4F61q8SoL+mqTypId2T2ZpuxosNSgowiCnLp2+Z+rivdU0AqpfgiD7yCBg==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.1.16': + resolution: {integrity: sha512-2OSv52FRuhdlgyOQqgtQHuCgXnS8nFSYRp2tJ+4WZXKgTxqPy7SMSls8c3mPT5pkZ17SBToGM5LHEJBO7miEdg==} + engines: {node: '>= 10'} + + '@tailwindcss/postcss@4.1.16': + resolution: {integrity: sha512-Qn3SFGPXYQMKR/UtqS+dqvPrzEeBZHrFA92maT4zijCVggdsXnDBMsPFJo1eArX3J+O+Gi+8pV4PkqjLCNBk3A==} + + '@tanstack/query-core@5.90.5': + resolution: {integrity: sha512-wLamYp7FaDq6ZnNehypKI5fNvxHPfTYylE0m/ZpuuzJfJqhR5Pxg9gvGBHZx4n7J+V5Rg5mZxHHTlv25Zt5u+w==} + + '@tanstack/react-query@5.90.5': + resolution: {integrity: sha512-pN+8UWpxZkEJ/Rnnj2v2Sxpx1WFlaa9L6a4UO89p6tTQbeo+m0MS8oYDjbggrR8QcTyjKoYWKS3xJQGr3ExT8Q==} + peerDependencies: + react: ^18 || ^19 + + '@tanstack/react-virtual@3.13.12': + resolution: {integrity: sha512-Gd13QdxPSukP8ZrkbgS2RwoZseTTbQPLnQEn7HY/rqtM+8Zt95f7xKC7N0EsKs7aoz0WzZ+fditZux+F8EzYxA==} + 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 + + '@tanstack/virtual-core@3.13.12': + resolution: {integrity: sha512-1YBOJfRHV4sXUmWsFSf5rQor4Ss82G8dQWLRbnk3GA4jeP8hQt1hxXh0tmflpC0dz3VgEv/1+qwPyLeWkQuPFA==} + + '@tybys/wasm-util@0.10.1': + resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/json5@0.0.29': + resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} + + '@types/node@20.19.23': + resolution: {integrity: sha512-yIdlVVVHXpmqRhtyovZAcSy0MiPcYWGkoO4CGe/+jpP0hmNuihm4XhHbADpK++MsiLHP5MVlv+bcgdF99kSiFQ==} + + '@types/react-dom@19.2.2': + resolution: {integrity: sha512-9KQPoO6mZCi7jcIStSnlOWn2nEF3mNmyr3rIAsGnAbQKYbRLyqmeSc39EVgtxXVia+LMT8j3knZLAZAh+xLmrw==} + peerDependencies: + '@types/react': ^19.2.0 + + '@types/react@19.2.2': + resolution: {integrity: sha512-6mDvHUFSjyT2B2yeNx2nUgMxh9LtOWvkhIU3uePn2I2oyNymUAX1NIsdgviM4CH+JSrp2D2hsMvJOkxY+0wNRA==} + + '@typescript-eslint/eslint-plugin@8.46.2': + resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.46.2 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/parser@8.46.2': + resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/project-service@8.46.2': + resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/scope-manager@8.46.2': + resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.46.2': + resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/type-utils@8.46.2': + resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/types@8.46.2': + resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.46.2': + resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/utils@8.46.2': + resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <6.0.0' + + '@typescript-eslint/visitor-keys@8.46.2': + resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + cpu: [arm] + os: [android] + + '@unrs/resolver-binding-android-arm64@1.11.1': + resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + cpu: [arm64] + os: [android] + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + cpu: [arm64] + os: [darwin] + + '@unrs/resolver-binding-darwin-x64@1.11.1': + resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + cpu: [x64] + os: [darwin] + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + cpu: [x64] + os: [freebsd] + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + cpu: [arm] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + cpu: [arm64] + os: [linux] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + cpu: [ppc64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + cpu: [riscv64] + os: [linux] + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + cpu: [s390x] + os: [linux] + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + cpu: [x64] + os: [linux] + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + cpu: [arm64] + os: [win32] + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + cpu: [ia32] + os: [win32] + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + cpu: [x64] + os: [win32] + + '@vitest/expect@4.0.14': + resolution: {integrity: sha512-RHk63V3zvRiYOWAV0rGEBRO820ce17hz7cI2kDmEdfQsBjT2luEKB5tCOc91u1oSQoUOZkSv3ZyzkdkSLD7lKw==} + + '@vitest/mocker@4.0.14': + resolution: {integrity: sha512-RzS5NujlCzeRPF1MK7MXLiEFpkIXeMdQ+rN3Kk3tDI9j0mtbr7Nmuq67tpkOJQpgyClbOltCXMjLZicJHsH5Cg==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.0.14': + resolution: {integrity: sha512-SOYPgujB6TITcJxgd3wmsLl+wZv+fy3av2PpiPpsWPZ6J1ySUYfScfpIt2Yv56ShJXR2MOA6q2KjKHN4EpdyRQ==} + + '@vitest/runner@4.0.14': + resolution: {integrity: sha512-BsAIk3FAqxICqREbX8SetIteT8PiaUL/tgJjmhxJhCsigmzzH8xeadtp7LRnTpCVzvf0ib9BgAfKJHuhNllKLw==} + + '@vitest/snapshot@4.0.14': + resolution: {integrity: sha512-aQVBfT1PMzDSA16Y3Fp45a0q8nKexx6N5Amw3MX55BeTeZpoC08fGqEZqVmPcqN0ueZsuUQ9rriPMhZ3Mu19Ag==} + + '@vitest/spy@4.0.14': + resolution: {integrity: sha512-JmAZT1UtZooO0tpY3GRyiC/8W7dCs05UOq9rfsUUgEZEdq+DuHLmWhPsrTt0TiW7WYeL/hXpaE07AZ2RCk44hg==} + + '@vitest/utils@4.0.14': + resolution: {integrity: sha512-hLqXZKAWNg8pI+SQXyXxWCTOpA3MvsqcbVeNgSi8x/CSN2wi26dSzn1wrOhmCmFjEvN9p8/kLFRHa6PI8jHazw==} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + + aria-query@5.3.2: + resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} + engines: {node: '>= 0.4'} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + array-includes@3.1.9: + resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlast@1.2.5: + resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==} + engines: {node: '>= 0.4'} + + array.prototype.findlastindex@1.2.6: + resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==} + engines: {node: '>= 0.4'} + + array.prototype.flat@1.3.3: + resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} + engines: {node: '>= 0.4'} + + array.prototype.flatmap@1.3.3: + resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} + engines: {node: '>= 0.4'} + + array.prototype.tosorted@1.1.4: + resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + ast-types-flow@0.0.8: + resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + axe-core@4.11.0: + resolution: {integrity: sha512-ilYanEU8vxxBexpJd8cWM4ElSQq4QctCLKih0TSfjIfCQTeyH/6zVrmIJfLPrKTKJRbiG+cfnZbQIjAlJmF1jQ==} + engines: {node: '>=4'} + + axios@1.12.2: + resolution: {integrity: sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==} + + axobject-query@4.1.0: + resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} + engines: {node: '>= 0.4'} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + caniuse-lite@1.0.30001751: + resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==} + + chai@6.2.1: + resolution: {integrity: sha512-p4Z49OGG5W/WBCPSS/dH3jQ73kD6tiMmUM+bckNK6Jr5JHMG3k9bg/BvKR8lKmtVBKmOiuVaV2ws8s9oSbwysg==} + engines: {node: '>=18'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + class-variance-authority@0.7.1: + resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} + + client-only@0.0.1: + resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + damerau-levenshtein@1.0.8: + resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + date-fns-jalali@4.1.0-0: + resolution: {integrity: sha512-hTIP/z+t+qKwBDcmmsnmjWTduxCg+5KfdqWQvb2X/8C9+knYY6epN/pfxdDuyVlSVeFz0sM5eEfwIUQ70U4ckg==} + + date-fns@4.1.0: + resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + + debug@3.2.7: + resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + doctrine@2.1.0: + resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==} + engines: {node: '>=0.10.0'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + embla-carousel-react@8.6.0: + resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} + peerDependencies: + react: ^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + + embla-carousel-reactive-utils@8.6.0: + resolution: {integrity: sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A==} + peerDependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: + resolution: {integrity: sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA==} + + emoji-picker-react@4.14.2: + resolution: {integrity: sha512-tQfuZ5pqZnaqh/aGtgjp5iFqEKC2SoFOSs7YxbnSignstlIwNRS/Peh/Y1pFSMoODwS1DH4i0tdnod5/B/LwBw==} + engines: {node: '>=10'} + peerDependencies: + react: '>=16' + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.18.3: + resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==} + engines: {node: '>=10.13.0'} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-iterator-helpers@1.2.1: + resolution: {integrity: sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==} + engines: {node: '>= 0.4'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-shim-unscopables@1.1.0: + resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + eslint-config-next@15.5.4: + resolution: {integrity: sha512-BzgVVuT3kfJes8i2GHenC1SRJ+W3BTML11lAOYFOOPzrk2xp66jBOAGEFRw+3LkYCln5UzvFsLhojrshb5Zfaw==} + peerDependencies: + eslint: ^7.23.0 || ^8.0.0 || ^9.0.0 + typescript: '>=3.3.1' + peerDependenciesMeta: + typescript: + optional: true + + eslint-config-prettier@10.1.8: + resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} + hasBin: true + peerDependencies: + eslint: '>=7.0.0' + + eslint-import-resolver-node@0.3.9: + resolution: {integrity: sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==} + + eslint-import-resolver-typescript@3.10.1: + resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + eslint: '*' + eslint-plugin-import: '*' + eslint-plugin-import-x: '*' + peerDependenciesMeta: + eslint-plugin-import: + optional: true + eslint-plugin-import-x: + optional: true + + eslint-module-utils@2.12.1: + resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: '*' + eslint-import-resolver-node: '*' + eslint-import-resolver-typescript: '*' + eslint-import-resolver-webpack: '*' + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + eslint: + optional: true + eslint-import-resolver-node: + optional: true + eslint-import-resolver-typescript: + optional: true + eslint-import-resolver-webpack: + optional: true + + eslint-plugin-import@2.32.0: + resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==} + engines: {node: '>=4'} + peerDependencies: + '@typescript-eslint/parser': '*' + eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9 + peerDependenciesMeta: + '@typescript-eslint/parser': + optional: true + + eslint-plugin-jsx-a11y@6.10.2: + resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} + engines: {node: '>=4.0'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 + + eslint-plugin-prettier@5.5.4: + resolution: {integrity: sha512-swNtI95SToIz05YINMA6Ox5R057IMAmWZ26GqPxusAp1TZzj+IdY9tXNWWD3vkF/wEqydCONcwjTFpxybBqZsg==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + '@types/eslint': '>=8.0.0' + eslint: '>=8.0.0' + eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' + prettier: '>=3.0.0' + peerDependenciesMeta: + '@types/eslint': + optional: true + eslint-config-prettier: + optional: true + + eslint-plugin-react-hooks@5.2.0: + resolution: {integrity: sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==} + engines: {node: '>=10'} + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + + eslint-plugin-react@7.37.5: + resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==} + engines: {node: '>=4'} + peerDependencies: + eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7 + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.38.0: + resolution: {integrity: sha512-t5aPOpmtJcZcz5UJyY2GbvpDlsK5E8JqRqoKtfiKE3cNh437KIqfJr3A3AKf5k64NPx6d0G3dno6XDY05PqPtw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + exifr@7.1.3: + resolution: {integrity: sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw==} + + expect-type@1.2.2: + resolution: {integrity: sha512-JhFGDVJ7tmDJItKhYgJCGLOWjuK9vPxiXoUFLwLDc99NlmklilbiQJwoctZtt13+xMw91MCk/REan6MWHqDjyA==} + engines: {node: '>=12.0.0'} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-diff@1.3.0: + resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} + + fast-glob@3.3.1: + resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} + engines: {node: '>=8.6.0'} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flairup@1.0.0: + resolution: {integrity: sha512-IKlE+pNvL2R+kVL1kEhUYqRxVqeFnjiIvHWDMLFXNaqyUdFXQM2wte44EfMYJNHkW16X991t2Zg8apKkhv7OBA==} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + follow-redirects@1.15.11: + resolution: {integrity: sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + form-data@4.0.4: + resolution: {integrity: sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==} + engines: {node: '>= 6'} + + framer-motion@12.23.24: + resolution: {integrity: sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w==} + peerDependencies: + '@emotion/is-prop-valid': '*' + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/is-prop-valid': + optional: true + react: + optional: true + react-dom: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + generator-function@2.0.1: + resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} + engines: {node: '>= 0.4'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + get-tsconfig@4.13.0: + resolution: {integrity: sha512-1VKTZJCwBrvbd+Wn3AOgQP/2Av+TfTCOlE4AcRJE72W1ksZXbAx8PPBR9RzgTeSPzlPMHrbANMH3LbltH73wxQ==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + heic-to@1.3.0: + resolution: {integrity: sha512-ptlhtnvVjhUu0aXhmp1Ic0iD7dFRGVGplwXu17odLogd1O+zFSvmMRMKKzAobYqMpkVXwbA1AlY+ZN49O3FJkQ==} + + html-to-image@1.11.13: + resolution: {integrity: sha512-cuOPoI7WApyhBElTTb9oqsawRvZ0rHhaHwghRLlTuffoD1B2aDemlCruLeZrUIIdvG7gs9xeELEPm6PhuASqrg==} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-bun-module@2.0.0: + resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-generator-function@1.1.2: + resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + iterator.prototype@1.1.5: + resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==} + engines: {node: '>= 0.4'} + + jiti@2.6.1: + resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + json5@1.0.2: + resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==} + hasBin: true + + jsx-ast-utils@3.3.5: + resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} + engines: {node: '>=4.0'} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + lightningcss-android-arm64@1.30.2: + resolution: {integrity: sha512-BH9sEdOCahSgmkVhBLeU7Hc9DWeZ1Eb6wNS6Da8igvUwAe0sqROHddIlvU06q3WyXVEOYDZ6ykBZQnjTbmo4+A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.30.2: + resolution: {integrity: sha512-ylTcDJBN3Hp21TdhRT5zBOIi73P6/W0qwvlFEk22fkdXchtNTOU4Qc37SkzV+EKYxLouZ6M4LG9NfZ1qkhhBWA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.30.2: + resolution: {integrity: sha512-oBZgKchomuDYxr7ilwLcyms6BCyLn0z8J0+ZZmfpjwg9fRVZIR5/GMXd7r9RH94iDhld3UmSjBM6nXWM2TfZTQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.30.2: + resolution: {integrity: sha512-c2bH6xTrf4BDpK8MoGG4Bd6zAMZDAXS569UxCAGcA7IKbHNMlhGQ89eRmvpIUGfKWNVdbhSbkQaWhEoMGmGslA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.30.2: + resolution: {integrity: sha512-eVdpxh4wYcm0PofJIZVuYuLiqBIakQ9uFZmipf6LF/HRj5Bgm0eb3qL/mr1smyXIS1twwOxNWndd8z0E374hiA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.30.2: + resolution: {integrity: sha512-UK65WJAbwIJbiBFXpxrbTNArtfuznvxAJw4Q2ZGlU8kPeDIWEX1dg3rn2veBVUylA2Ezg89ktszWbaQnxD/e3A==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.30.2: + resolution: {integrity: sha512-5Vh9dGeblpTxWHpOx8iauV02popZDsCYMPIgiuw97OJ5uaDsL86cnqSFs5LZkG3ghHoX5isLgWzMs+eD1YzrnA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.30.2: + resolution: {integrity: sha512-Cfd46gdmj1vQ+lR6VRTTadNHu6ALuw2pKR9lYq4FnhvgBc4zWY1EtZcAc6EffShbb1MFrIPfLDXD6Xprbnni4w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.30.2: + resolution: {integrity: sha512-XJaLUUFXb6/QG2lGIW6aIk6jKdtjtcffUT0NKvIqhSBY3hh9Ch+1LCeH80dR9q9LBjG3ewbDjnumefsLsP6aiA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.30.2: + resolution: {integrity: sha512-FZn+vaj7zLv//D/192WFFVA0RgHawIcHqLX9xuWiQt7P0PtdFEVaxgF9rjM/IRYHQXNnk61/H/gb2Ei+kUQ4xQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.30.2: + resolution: {integrity: sha512-5g1yc73p+iAkid5phb4oVFMB45417DkRevRbt/El/gKXJk4jid+vPFF/AXbxn05Aky8PapwzZrdJShv5C0avjw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.30.2: + resolution: {integrity: sha512-utfs7Pr5uJyyvDETitgsaqSyjCb2qNRAtuqUeWIAKztsOYdcACf2KtARYXg2pSvhkt+9NfoaNY7fxjl6nuMjIQ==} + engines: {node: '>= 12.0.0'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lottie-react@2.4.1: + resolution: {integrity: sha512-LQrH7jlkigIIv++wIyrOYFLHSKQpEY4zehPicL9bQsrt1rnoKRYCYgpCUe5maqylNtacy58/sQDZTkwMcTRxZw==} + 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 + + lottie-web@5.13.0: + resolution: {integrity: sha512-+gfBXl6sxXMPe8tKQm7qzLnUy5DUPJPKIyRHwtpCpyUEYjHYRJC/5gjUvdkuO2c3JllrPtHXH5UJJK8LRYl5yQ==} + + lucide-react@0.546.0: + resolution: {integrity: sha512-Z94u6fKT43lKeYHiVyvyR8fT7pwCzDu7RyMPpTvh054+xahSgj4HFQ+NmflvzdXsoAjYGdCguGaFKYuvq0ThCQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + motion-dom@12.23.23: + resolution: {integrity: sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA==} + + motion-utils@12.23.6: + resolution: {integrity: sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.11: + resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + napi-postinstall@0.3.4: + resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} + engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + hasBin: true + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + next@15.5.7: + resolution: {integrity: sha512-+t2/0jIJ48kUpGKkdlhgkv+zPTEOoXyr60qXe68eB/pl3CMJaLeIGjzp5D6Oqt25hCBiBTt8wEeeAzfJvUKnPQ==} + engines: {node: ^18.18.0 || ^19.8.0 || >= 20.0.0} + hasBin: true + peerDependencies: + '@opentelemetry/api': ^1.1.0 + '@playwright/test': ^1.51.1 + babel-plugin-react-compiler: '*' + react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0 + sass: ^1.3.0 + peerDependenciesMeta: + '@opentelemetry/api': + optional: true + '@playwright/test': + optional: true + babel-plugin-react-compiler: + optional: true + sass: + optional: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + object.entries@1.1.9: + resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==} + engines: {node: '>= 0.4'} + + object.fromentries@2.0.8: + resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} + engines: {node: '>= 0.4'} + + object.groupby@1.0.3: + resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==} + engines: {node: '>= 0.4'} + + object.values@1.2.1: + resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} + engines: {node: '>= 0.4'} + + obug@2.1.1: + resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + picomatch@4.0.3: + resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + engines: {node: '>=12'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + postcss@8.4.31: + resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} + engines: {node: ^10 || ^12 || >=14} + + postcss@8.5.6: + resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + engines: {node: ^10 || ^12 || >=14} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + prettier-linter-helpers@1.0.0: + resolution: {integrity: sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==} + engines: {node: '>=6.0.0'} + + prettier-plugin-tailwindcss@0.7.1: + resolution: {integrity: sha512-Bzv1LZcuiR1Sk02iJTS1QzlFNp/o5l2p3xkopwOrbPmtMeh3fK9rVW5M3neBQzHq+kGKj/4LGQMTNcTH4NGPtQ==} + engines: {node: '>=20.19'} + peerDependencies: + '@ianvs/prettier-plugin-sort-imports': '*' + '@prettier/plugin-hermes': '*' + '@prettier/plugin-oxc': '*' + '@prettier/plugin-pug': '*' + '@shopify/prettier-plugin-liquid': '*' + '@trivago/prettier-plugin-sort-imports': '*' + '@zackad/prettier-plugin-twig': '*' + prettier: ^3.0 + prettier-plugin-astro: '*' + prettier-plugin-css-order: '*' + prettier-plugin-jsdoc: '*' + prettier-plugin-marko: '*' + prettier-plugin-multiline-arrays: '*' + prettier-plugin-organize-attributes: '*' + prettier-plugin-organize-imports: '*' + prettier-plugin-sort-imports: '*' + prettier-plugin-svelte: '*' + peerDependenciesMeta: + '@ianvs/prettier-plugin-sort-imports': + optional: true + '@prettier/plugin-hermes': + optional: true + '@prettier/plugin-oxc': + optional: true + '@prettier/plugin-pug': + optional: true + '@shopify/prettier-plugin-liquid': + optional: true + '@trivago/prettier-plugin-sort-imports': + optional: true + '@zackad/prettier-plugin-twig': + optional: true + prettier-plugin-astro: + optional: true + prettier-plugin-css-order: + optional: true + prettier-plugin-jsdoc: + optional: true + prettier-plugin-marko: + optional: true + prettier-plugin-multiline-arrays: + optional: true + prettier-plugin-organize-attributes: + optional: true + prettier-plugin-organize-imports: + optional: true + prettier-plugin-sort-imports: + optional: true + prettier-plugin-svelte: + optional: true + + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + engines: {node: '>=14'} + hasBin: true + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + + proxy-from-env@1.1.0: + resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + qr.js@0.0.0: + resolution: {integrity: sha512-c4iYnWb+k2E+vYpRimHqSu575b1/wKl4XFeJGpFmrJQz5I88v9aY2czh7s0w36srfCM1sXgC/xpoJz5dJfq+OQ==} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + react-day-picker@9.11.1: + resolution: {integrity: sha512-l3ub6o8NlchqIjPKrRFUCkTUEq6KwemQlfv3XZzzwpUeGwmDJ+0u0Upmt38hJyd7D/vn2dQoOoLV/qAp0o3uUw==} + engines: {node: '>=18'} + peerDependencies: + react: '>=16.8.0' + + react-dom@19.1.2: + resolution: {integrity: sha512-dEoydsCp50i7kS1xHOmPXq4zQYoGWedUsvqv9H6zdif2r7yLHygyfP9qou71TulRN0d6ng9EbRVsQhSqfUc19g==} + peerDependencies: + react: ^19.1.2 + + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-qr-code@2.0.18: + resolution: {integrity: sha512-v1Jqz7urLMhkO6jkgJuBYhnqvXagzceg3qJUWayuCK/c6LTIonpWbwxR1f1APGd4xrW/QcQEovNrAojbUz65Tg==} + peerDependencies: + react: '*' + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.1: + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@19.1.2: + resolution: {integrity: sha512-MdWVitvLbQULD+4DP8GYjZUrepGW7d+GQkNVqJEzNxE+e9WIa4egVFE/RDfVb1u9u/Jw7dNMmPB4IqxzbFYJ0w==} + engines: {node: '>=0.10.0'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-pkg-maps@1.0.0: + resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} + + resolve@1.22.11: + resolution: {integrity: sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==} + engines: {node: '>= 0.4'} + hasBin: true + + resolve@2.0.0-next.5: + resolution: {integrity: sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==} + hasBin: true + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@4.53.3: + resolution: {integrity: sha512-w8GmOxZfBmKknvdXU1sdM9NHcoQejwF/4mNgj2JuEEdRaHwwF12K7e9eXn1nLZ07ad+du76mkVsyeb2rKGllsA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.7.3: + resolution: {integrity: sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + sharp@0.34.4: + resolution: {integrity: sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + stable-hash@0.0.5: + resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string.prototype.includes@2.0.1: + resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} + engines: {node: '>= 0.4'} + + string.prototype.matchall@4.0.12: + resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==} + engines: {node: '>= 0.4'} + + string.prototype.repeat@1.0.0: + resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + styled-jsx@5.1.6: + resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@babel/core': '*' + babel-plugin-macros: '*' + react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0' + peerDependenciesMeta: + '@babel/core': + optional: true + babel-plugin-macros: + optional: true + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + swiper@12.0.3: + resolution: {integrity: sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==} + engines: {node: '>= 4.7.0'} + + synckit@0.11.11: + resolution: {integrity: sha512-MeQTA1r0litLUf0Rp/iisCaL8761lKAZHaimlbGK4j0HysC4PLfqygQj9srcs0m2RdtDYnF8UuYyKpbjHYp7Jw==} + engines: {node: ^14.18.0 || >=16.0.0} + + tabbable@6.3.0: + resolution: {integrity: sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ==} + + tailwind-merge@3.3.1: + resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==} + + tailwindcss@4.1.16: + resolution: {integrity: sha512-pONL5awpaQX4LN5eiv7moSiSPd/DLDzKVRJz8Q9PgzmAdd1R4307GQS2ZpfiN7ZmekdQrfhZZiSE5jkLR4WNaA==} + + tapable@2.3.0: + resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==} + engines: {node: '>=6'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.15: + resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.0.3: + resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + engines: {node: '>=14.0.0'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + tsconfig-paths@3.15.0: + resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==} + + tslib@2.8.1: + resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} + + tw-animate-css@1.4.0: + resolution: {integrity: sha512-7bziOlRqH0hJx80h/3mbicLW7o8qLsH5+RaLR2t+OHM3D0JlWGODQKQ4cxbK7WlvmUxpcj6Kgu6EKqjrGFe3QQ==} + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unrs-resolver@1.11.1: + resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + vaul@1.1.2: + resolution: {integrity: sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc + + vite@7.2.4: + resolution: {integrity: sha512-NL8jTlbo0Tn4dUEXEsUg8KeyG/Lkmc4Fnzb8JXN/Ykm9G4HNImjtABMJgkQoVjOBN/j2WAwDTRytdqJbZsah7w==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + jiti: '>=1.21.0' + less: ^4.0.0 + lightningcss: ^1.21.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.0.14: + resolution: {integrity: sha512-d9B2J9Cm9dN9+6nxMnnNJKJCtcyKfnHj15N6YNJfaFHRLua/d3sRKU9RuKmO9mB0XdFtUizlxfz/VPbd3OxGhw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.0.14 + '@vitest/browser-preview': 4.0.14 + '@vitest/browser-webdriverio': 4.0.14 + '@vitest/ui': 4.0.14 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zustand@5.0.8: + resolution: {integrity: sha512-gyPKpIaxY9XcO2vSMrLbiER7QMAMGOQZVRdJ6Zi782jkbzZygq5GI9nG8g+sMgitRtndwaBSl7uiqC49o1SSiw==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@date-fns/tz@1.4.1': {} + + '@emnapi/core@1.6.0': + dependencies: + '@emnapi/wasi-threads': 1.1.0 + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.6.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/wasi-threads@1.1.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@eslint-community/eslint-utils@4.9.0(eslint@9.38.0(jiti@2.6.1))': + dependencies: + eslint: 9.38.0(jiti@2.6.1) + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.2': {} + + '@eslint/config-array@0.21.1': + dependencies: + '@eslint/object-schema': 2.1.7 + debug: 4.4.3 + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.4.1': + dependencies: + '@eslint/core': 0.16.0 + + '@eslint/core@0.16.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.3 + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.38.0': {} + + '@eslint/object-schema@2.1.7': {} + + '@eslint/plugin-kit@0.4.0': + dependencies: + '@eslint/core': 0.16.0 + levn: 0.4.1 + + '@floating-ui/core@1.7.3': + dependencies: + '@floating-ui/utils': 0.2.10 + + '@floating-ui/dom@1.7.4': + dependencies: + '@floating-ui/core': 1.7.3 + '@floating-ui/utils': 0.2.10 + + '@floating-ui/react-dom@2.1.6(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@floating-ui/dom': 1.7.4 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + '@floating-ui/react@0.26.28(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@floating-ui/utils': 0.2.10 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + tabbable: 6.3.0 + + '@floating-ui/utils@0.2.10': {} + + '@headlessui/react@2.2.9(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@floating-ui/react': 0.26.28(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@react-aria/focus': 3.21.2(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@react-aria/interactions': 3.25.6(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@tanstack/react-virtual': 3.13.12(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + use-sync-external-store: 1.6.0(react@19.1.2) + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.7': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.4.3 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/colour@1.0.0': + optional: true + + '@img/sharp-darwin-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.2.3 + optional: true + + '@img/sharp-darwin-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.2.3 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-darwin-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-arm@1.2.3': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.2.3': + optional: true + + '@img/sharp-libvips-linux-s390x@1.2.3': + optional: true + + '@img/sharp-libvips-linux-x64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.2.3': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.2.3': + optional: true + + '@img/sharp-linux-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.2.3 + optional: true + + '@img/sharp-linux-arm@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.2.3 + optional: true + + '@img/sharp-linux-ppc64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.2.3 + optional: true + + '@img/sharp-linux-s390x@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.2.3 + optional: true + + '@img/sharp-linux-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-arm64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + optional: true + + '@img/sharp-linuxmusl-x64@0.34.4': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + optional: true + + '@img/sharp-wasm32@0.34.4': + dependencies: + '@emnapi/runtime': 1.6.0 + optional: true + + '@img/sharp-win32-arm64@0.34.4': + optional: true + + '@img/sharp-win32-ia32@0.34.4': + optional: true + + '@img/sharp-win32-x64@0.34.4': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/wasm-runtime@0.2.12': + dependencies: + '@emnapi/core': 1.6.0 + '@emnapi/runtime': 1.6.0 + '@tybys/wasm-util': 0.10.1 + optional: true + + '@next/env@15.5.7': {} + + '@next/eslint-plugin-next@15.5.4': + dependencies: + fast-glob: 3.3.1 + + '@next/swc-darwin-arm64@15.5.7': + optional: true + + '@next/swc-darwin-x64@15.5.7': + optional: true + + '@next/swc-linux-arm64-gnu@15.5.7': + optional: true + + '@next/swc-linux-arm64-musl@15.5.7': + optional: true + + '@next/swc-linux-x64-gnu@15.5.7': + optional: true + + '@next/swc-linux-x64-musl@15.5.7': + optional: true + + '@next/swc-win32-arm64-msvc@15.5.7': + optional: true + + '@next/swc-win32-x64-msvc@15.5.7': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@nolyfill/is-core-module@1.0.39': {} + + '@pkgr/core@0.2.9': {} + + '@radix-ui/primitive@1.1.3': {} + + '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.2)(react@19.1.2)': + dependencies: + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-context@1.1.2(@types/react@19.2.2)(react@19.1.2)': + dependencies: + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.2) + aria-hidden: 1.2.6 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.2)(react@19.1.2)': + dependencies: + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-id@1.1.1(@types/react@19.2.2)(react@19.1.2)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/primitive': 1.1.3 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-id': 1.1.1(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.2)(react@19.1.2) + aria-hidden: 1.2.6 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + react-remove-scroll: 2.7.1(@types/react@19.2.2)(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@floating-ui/react-dom': 2.1.6(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-context': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/rect': 1.1.1 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + '@types/react-dom': 19.2.2(@types/react@19.2.2) + + '@radix-ui/react-slot@1.2.3(@types/react@19.2.2)(react@19.1.2)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.2)(react@19.1.2)': + dependencies: + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.2)(react@19.1.2)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.2)(react@19.1.2) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.2)(react@19.1.2)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.2)(react@19.1.2)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.2)(react@19.1.2)': + dependencies: + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.2)(react@19.1.2)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/react-use-size@1.1.1(@types/react@19.2.2)(react@19.1.2)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.2)(react@19.1.2) + react: 19.1.2 + optionalDependencies: + '@types/react': 19.2.2 + + '@radix-ui/rect@1.1.1': {} + + '@react-aria/focus@3.21.2(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@react-aria/interactions': 3.25.6(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@react-aria/utils': 3.31.0(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@react-types/shared': 3.32.1(react@19.1.2) + '@swc/helpers': 0.5.15 + clsx: 2.1.1 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + '@react-aria/interactions@3.25.6(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@react-aria/ssr': 3.9.10(react@19.1.2) + '@react-aria/utils': 3.31.0(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + '@react-stately/flags': 3.1.2 + '@react-types/shared': 3.32.1(react@19.1.2) + '@swc/helpers': 0.5.15 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + '@react-aria/ssr@3.9.10(react@19.1.2)': + dependencies: + '@swc/helpers': 0.5.15 + react: 19.1.2 + + '@react-aria/utils@3.31.0(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@react-aria/ssr': 3.9.10(react@19.1.2) + '@react-stately/flags': 3.1.2 + '@react-stately/utils': 3.10.8(react@19.1.2) + '@react-types/shared': 3.32.1(react@19.1.2) + '@swc/helpers': 0.5.15 + clsx: 2.1.1 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + '@react-stately/flags@3.1.2': + dependencies: + '@swc/helpers': 0.5.15 + + '@react-stately/utils@3.10.8(react@19.1.2)': + dependencies: + '@swc/helpers': 0.5.15 + react: 19.1.2 + + '@react-types/shared@3.32.1(react@19.1.2)': + dependencies: + react: 19.1.2 + + '@rollup/rollup-android-arm-eabi@4.53.3': + optional: true + + '@rollup/rollup-android-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-arm64@4.53.3': + optional: true + + '@rollup/rollup-darwin-x64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-arm64@4.53.3': + optional: true + + '@rollup/rollup-freebsd-x64@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.53.3': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-linux-x64-musl@4.53.3': + optional: true + + '@rollup/rollup-openharmony-arm64@4.53.3': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.53.3': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.53.3': + optional: true + + '@rtsao/scc@1.1.0': {} + + '@rushstack/eslint-patch@1.14.1': {} + + '@standard-schema/spec@1.0.0': {} + + '@swc/helpers@0.5.15': + dependencies: + tslib: 2.8.1 + + '@tailwindcss/node@4.1.16': + dependencies: + '@jridgewell/remapping': 2.3.5 + 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.16 + + '@tailwindcss/oxide-android-arm64@4.1.16': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.1.16': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.1.16': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.1.16': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.16': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.1.16': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.1.16': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.1.16': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.1.16': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.1.16': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.1.16': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.1.16': + optional: true + + '@tailwindcss/oxide@4.1.16': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.1.16 + '@tailwindcss/oxide-darwin-arm64': 4.1.16 + '@tailwindcss/oxide-darwin-x64': 4.1.16 + '@tailwindcss/oxide-freebsd-x64': 4.1.16 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.16 + '@tailwindcss/oxide-linux-arm64-gnu': 4.1.16 + '@tailwindcss/oxide-linux-arm64-musl': 4.1.16 + '@tailwindcss/oxide-linux-x64-gnu': 4.1.16 + '@tailwindcss/oxide-linux-x64-musl': 4.1.16 + '@tailwindcss/oxide-wasm32-wasi': 4.1.16 + '@tailwindcss/oxide-win32-arm64-msvc': 4.1.16 + '@tailwindcss/oxide-win32-x64-msvc': 4.1.16 + + '@tailwindcss/postcss@4.1.16': + dependencies: + '@alloc/quick-lru': 5.2.0 + '@tailwindcss/node': 4.1.16 + '@tailwindcss/oxide': 4.1.16 + postcss: 8.5.6 + tailwindcss: 4.1.16 + + '@tanstack/query-core@5.90.5': {} + + '@tanstack/react-query@5.90.5(react@19.1.2)': + dependencies: + '@tanstack/query-core': 5.90.5 + react: 19.1.2 + + '@tanstack/react-virtual@3.13.12(react-dom@19.1.2(react@19.1.2))(react@19.1.2)': + dependencies: + '@tanstack/virtual-core': 3.13.12 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + '@tanstack/virtual-core@3.13.12': {} + + '@tybys/wasm-util@0.10.1': + dependencies: + tslib: 2.8.1 + optional: true + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.8': {} + + '@types/json-schema@7.0.15': {} + + '@types/json5@0.0.29': {} + + '@types/node@20.19.23': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.2.2(@types/react@19.2.2)': + dependencies: + '@types/react': 19.2.2 + + '@types/react@19.2.2': + dependencies: + csstype: 3.1.3 + + '@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/regexpp': 4.12.2 + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/type-utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + eslint: 9.38.0(jiti@2.6.1) + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 + eslint: 9.38.0(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.46.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + debug: 4.4.3 + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.46.2': + dependencies: + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 + + '@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)': + dependencies: + typescript: 5.9.3 + + '@typescript-eslint/type-utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + '@typescript-eslint/utils': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + debug: 4.4.3 + eslint: 9.38.0(jiti@2.6.1) + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.46.2': {} + + '@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)': + dependencies: + '@typescript-eslint/project-service': 8.46.2(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3) + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/visitor-keys': 8.46.2 + debug: 4.4.3 + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.3 + ts-api-utils: 2.1.0(typescript@5.9.3) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3)': + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1)) + '@typescript-eslint/scope-manager': 8.46.2 + '@typescript-eslint/types': 8.46.2 + '@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) + typescript: 5.9.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.46.2': + dependencies: + '@typescript-eslint/types': 8.46.2 + eslint-visitor-keys: 4.2.1 + + '@unrs/resolver-binding-android-arm-eabi@1.11.1': + optional: true + + '@unrs/resolver-binding-android-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-arm64@1.11.1': + optional: true + + '@unrs/resolver-binding-darwin-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-freebsd-x64@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.11.1': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.11.1': + dependencies: + '@napi-rs/wasm-runtime': 0.2.12 + optional: true + + '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + optional: true + + '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + optional: true + + '@vitest/expect@4.0.14': + dependencies: + '@standard-schema/spec': 1.0.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 + chai: 6.2.1 + tinyrainbow: 3.0.3 + + '@vitest/mocker@4.0.14(vite@7.2.4(@types/node@20.19.23)(jiti@2.6.1)(lightningcss@1.30.2))': + dependencies: + '@vitest/spy': 4.0.14 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 7.2.4(@types/node@20.19.23)(jiti@2.6.1)(lightningcss@1.30.2) + + '@vitest/pretty-format@4.0.14': + dependencies: + tinyrainbow: 3.0.3 + + '@vitest/runner@4.0.14': + dependencies: + '@vitest/utils': 4.0.14 + pathe: 2.0.3 + + '@vitest/snapshot@4.0.14': + dependencies: + '@vitest/pretty-format': 4.0.14 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.0.14': {} + + '@vitest/utils@4.0.14': + dependencies: + '@vitest/pretty-format': 4.0.14 + tinyrainbow: 3.0.3 + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + argparse@2.0.1: {} + + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + + aria-query@5.3.2: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + array-includes@3.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + is-string: 1.1.1 + math-intrinsics: 1.1.0 + + array.prototype.findlast@1.2.5: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.findlastindex@1.2.6: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-shim-unscopables: 1.1.0 + + array.prototype.flat@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.flatmap@1.3.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-shim-unscopables: 1.1.0 + + array.prototype.tosorted@1.1.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-shim-unscopables: 1.1.0 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + assertion-error@2.0.1: {} + + ast-types-flow@0.0.8: {} + + async-function@1.0.0: {} + + asynckit@0.4.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + axe-core@4.11.0: {} + + axios@1.12.2: + dependencies: + follow-redirects: 1.15.11 + form-data: 4.0.4 + proxy-from-env: 1.1.0 + transitivePeerDependencies: + - debug + + axobject-query@4.1.0: {} + + balanced-match@1.0.2: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + caniuse-lite@1.0.30001751: {} + + chai@6.2.1: {} + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + class-variance-authority@0.7.1: + dependencies: + clsx: 2.1.1 + + client-only@0.0.1: {} + + clsx@2.1.1: {} + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.4: {} + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + concat-map@0.0.1: {} + + core-util-is@1.0.3: {} + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + damerau-levenshtein@1.0.8: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + date-fns-jalali@4.1.0-0: {} + + date-fns@4.1.0: {} + + debug@3.2.7: + dependencies: + ms: 2.1.3 + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + delayed-stream@1.0.0: {} + + detect-libc@2.1.2: {} + + detect-node-es@1.1.0: {} + + doctrine@2.1.0: + dependencies: + esutils: 2.0.3 + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + embla-carousel-react@8.6.0(react@19.1.2): + dependencies: + embla-carousel: 8.6.0 + embla-carousel-reactive-utils: 8.6.0(embla-carousel@8.6.0) + react: 19.1.2 + + embla-carousel-reactive-utils@8.6.0(embla-carousel@8.6.0): + dependencies: + embla-carousel: 8.6.0 + + embla-carousel@8.6.0: {} + + emoji-picker-react@4.14.2(react@19.1.2): + dependencies: + flairup: 1.0.0 + react: 19.1.2 + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.18.3: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.0 + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-iterator-helpers@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-set-tostringtag: 2.1.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + iterator.prototype: 1.1.5 + safe-array-concat: 1.1.3 + + es-module-lexer@1.7.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-shim-unscopables@1.1.0: + dependencies: + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escape-string-regexp@4.0.0: {} + + eslint-config-next@15.5.4(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 15.5.4 + '@rushstack/eslint-patch': 1.14.1 + '@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-react: 7.37.5(eslint@9.38.0(jiti@2.6.1)) + eslint-plugin-react-hooks: 5.2.0(eslint@9.38.0(jiti@2.6.1)) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + + eslint-config-prettier@10.1.8(eslint@9.38.0(jiti@2.6.1)): + dependencies: + eslint: 9.38.0(jiti@2.6.1) + + eslint-import-resolver-node@0.3.9: + dependencies: + debug: 3.2.7 + is-core-module: 2.16.1 + resolve: 1.22.11 + transitivePeerDependencies: + - supports-color + + eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.38.0(jiti@2.6.1)): + dependencies: + '@nolyfill/is-core-module': 1.0.39 + debug: 4.4.3 + eslint: 9.38.0(jiti@2.6.1) + get-tsconfig: 4.13.0 + is-bun-module: 2.0.0 + stable-hash: 0.0.5 + tinyglobby: 0.2.15 + unrs-resolver: 1.11.1 + optionalDependencies: + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)): + dependencies: + debug: 3.2.7 + optionalDependencies: + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + eslint: 9.38.0(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.38.0(jiti@2.6.1)) + transitivePeerDependencies: + - supports-color + + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)): + dependencies: + '@rtsao/scc': 1.1.0 + array-includes: 3.1.9 + array.prototype.findlastindex: 1.2.6 + array.prototype.flat: 1.3.3 + array.prototype.flatmap: 1.3.3 + debug: 3.2.7 + doctrine: 2.1.0 + eslint: 9.38.0(jiti@2.6.1) + eslint-import-resolver-node: 0.3.9 + eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.38.0(jiti@2.6.1)) + hasown: 2.0.2 + is-core-module: 2.16.1 + is-glob: 4.0.3 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + object.groupby: 1.0.3 + object.values: 1.2.1 + semver: 6.3.1 + string.prototype.trimend: 1.0.9 + tsconfig-paths: 3.15.0 + optionalDependencies: + '@typescript-eslint/parser': 8.46.2(eslint@9.38.0(jiti@2.6.1))(typescript@5.9.3) + transitivePeerDependencies: + - eslint-import-resolver-typescript + - eslint-import-resolver-webpack + - supports-color + + eslint-plugin-jsx-a11y@6.10.2(eslint@9.38.0(jiti@2.6.1)): + dependencies: + aria-query: 5.3.2 + array-includes: 3.1.9 + array.prototype.flatmap: 1.3.3 + ast-types-flow: 0.0.8 + axe-core: 4.11.0 + axobject-query: 4.1.0 + damerau-levenshtein: 1.0.8 + emoji-regex: 9.2.2 + eslint: 9.38.0(jiti@2.6.1) + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + language-tags: 1.0.9 + minimatch: 3.1.2 + object.fromentries: 2.0.8 + safe-regex-test: 1.1.0 + string.prototype.includes: 2.0.1 + + eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.38.0(jiti@2.6.1)))(eslint@9.38.0(jiti@2.6.1))(prettier@3.6.2): + dependencies: + eslint: 9.38.0(jiti@2.6.1) + prettier: 3.6.2 + prettier-linter-helpers: 1.0.0 + synckit: 0.11.11 + optionalDependencies: + eslint-config-prettier: 10.1.8(eslint@9.38.0(jiti@2.6.1)) + + eslint-plugin-react-hooks@5.2.0(eslint@9.38.0(jiti@2.6.1)): + dependencies: + eslint: 9.38.0(jiti@2.6.1) + + eslint-plugin-react@7.37.5(eslint@9.38.0(jiti@2.6.1)): + dependencies: + array-includes: 3.1.9 + array.prototype.findlast: 1.2.5 + array.prototype.flatmap: 1.3.3 + array.prototype.tosorted: 1.1.4 + doctrine: 2.1.0 + es-iterator-helpers: 1.2.1 + eslint: 9.38.0(jiti@2.6.1) + estraverse: 5.3.0 + hasown: 2.0.2 + jsx-ast-utils: 3.3.5 + minimatch: 3.1.2 + object.entries: 1.1.9 + object.fromentries: 2.0.8 + object.values: 1.2.1 + prop-types: 15.8.1 + resolve: 2.0.0-next.5 + semver: 6.3.1 + string.prototype.matchall: 4.0.12 + string.prototype.repeat: 1.0.0 + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.38.0(jiti@2.6.1): + dependencies: + '@eslint-community/eslint-utils': 4.9.0(eslint@9.38.0(jiti@2.6.1)) + '@eslint-community/regexpp': 4.12.2 + '@eslint/config-array': 0.21.1 + '@eslint/config-helpers': 0.4.1 + '@eslint/core': 0.16.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.38.0 + '@eslint/plugin-kit': 0.4.0 + '@humanfs/node': 0.16.7 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.3 + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + optionalDependencies: + jiti: 2.6.1 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.8 + + esutils@2.0.3: {} + + exifr@7.1.3: {} + + expect-type@1.2.2: {} + + fast-deep-equal@3.1.3: {} + + fast-diff@1.3.0: {} + + fast-glob@3.3.1: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + fdir@6.5.0(picomatch@4.0.3): + optionalDependencies: + picomatch: 4.0.3 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flairup@1.0.0: {} + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flatted@3.3.3: {} + + follow-redirects@1.15.11: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + form-data@4.0.4: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.2 + mime-types: 2.1.35 + + framer-motion@12.23.24(react-dom@19.1.2(react@19.1.2))(react@19.1.2): + dependencies: + motion-dom: 12.23.23 + motion-utils: 12.23.6 + tslib: 2.8.1 + optionalDependencies: + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + generator-function@2.0.1: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-nonce@1.0.1: {} + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + get-tsconfig@4.13.0: + dependencies: + resolve-pkg-maps: 1.0.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-bigints@1.1.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + heic-to@1.3.0: {} + + html-to-image@1.11.13: {} + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inherits@2.0.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-bun-module@2.0.0: + dependencies: + semver: 7.7.3 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-generator-function@1.1.2: + dependencies: + call-bound: 1.0.4 + generator-function: 2.0.1 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + iterator.prototype@1.1.5: + dependencies: + define-data-property: 1.1.4 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + has-symbols: 1.1.0 + set-function-name: 2.0.2 + + jiti@2.6.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + json5@1.0.2: + dependencies: + minimist: 1.2.8 + + jsx-ast-utils@3.3.5: + dependencies: + array-includes: 3.1.9 + array.prototype.flat: 1.3.3 + object.assign: 4.1.7 + object.values: 1.2.1 + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + lightningcss-android-arm64@1.30.2: + optional: true + + lightningcss-darwin-arm64@1.30.2: + optional: true + + lightningcss-darwin-x64@1.30.2: + optional: true + + lightningcss-freebsd-x64@1.30.2: + optional: true + + lightningcss-linux-arm-gnueabihf@1.30.2: + optional: true + + lightningcss-linux-arm64-gnu@1.30.2: + optional: true + + lightningcss-linux-arm64-musl@1.30.2: + optional: true + + lightningcss-linux-x64-gnu@1.30.2: + optional: true + + lightningcss-linux-x64-musl@1.30.2: + optional: true + + lightningcss-win32-arm64-msvc@1.30.2: + optional: true + + lightningcss-win32-x64-msvc@1.30.2: + optional: true + + lightningcss@1.30.2: + dependencies: + detect-libc: 2.1.2 + 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: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lottie-react@2.4.1(react-dom@19.1.2(react@19.1.2))(react@19.1.2): + dependencies: + lottie-web: 5.13.0 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + + lottie-web@5.13.0: {} + + lucide-react@0.546.0(react@19.1.2): + dependencies: + react: 19.1.2 + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minimist@1.2.8: {} + + motion-dom@12.23.23: + dependencies: + motion-utils: 12.23.6 + + motion-utils@12.23.6: {} + + ms@2.1.3: {} + + nanoid@3.3.11: {} + + napi-postinstall@0.3.4: {} + + natural-compare@1.4.0: {} + + next@15.5.7(react-dom@19.1.2(react@19.1.2))(react@19.1.2): + dependencies: + '@next/env': 15.5.7 + '@swc/helpers': 0.5.15 + caniuse-lite: 1.0.30001751 + postcss: 8.4.31 + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + styled-jsx: 5.1.6(react@19.1.2) + optionalDependencies: + '@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.4 + transitivePeerDependencies: + - '@babel/core' + - babel-plugin-macros + + object-assign@4.1.1: {} + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + object.entries@1.1.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + object.fromentries@2.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + + object.groupby@1.0.3: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + + object.values@1.2.1: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + obug@2.1.1: {} + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + pako@1.0.11: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + path-exists@4.0.0: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@2.3.1: {} + + picomatch@4.0.3: {} + + possible-typed-array-names@1.1.0: {} + + postcss@8.4.31: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + postcss@8.5.6: + dependencies: + nanoid: 3.3.11 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + prelude-ls@1.2.1: {} + + prettier-linter-helpers@1.0.0: + dependencies: + fast-diff: 1.3.0 + + prettier-plugin-tailwindcss@0.7.1(prettier@3.6.2): + dependencies: + prettier: 3.6.2 + + prettier@3.6.2: {} + + process-nextick-args@2.0.1: {} + + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + + proxy-from-env@1.1.0: {} + + punycode@2.3.1: {} + + qr.js@0.0.0: {} + + queue-microtask@1.2.3: {} + + react-day-picker@9.11.1(react@19.1.2): + dependencies: + '@date-fns/tz': 1.4.1 + date-fns: 4.1.0 + date-fns-jalali: 4.1.0-0 + react: 19.1.2 + + react-dom@19.1.2(react@19.1.2): + dependencies: + react: 19.1.2 + scheduler: 0.26.0 + + react-is@16.13.1: {} + + react-qr-code@2.0.18(react@19.1.2): + dependencies: + prop-types: 15.8.1 + qr.js: 0.0.0 + react: 19.1.2 + + react-remove-scroll-bar@2.3.8(@types/react@19.2.2)(react@19.1.2): + dependencies: + react: 19.1.2 + react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.1.2) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + react-remove-scroll@2.7.1(@types/react@19.2.2)(react@19.1.2): + dependencies: + react: 19.1.2 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.2)(react@19.1.2) + react-style-singleton: 2.2.3(@types/react@19.2.2)(react@19.1.2) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@19.2.2)(react@19.1.2) + use-sidecar: 1.1.3(@types/react@19.2.2)(react@19.1.2) + optionalDependencies: + '@types/react': 19.2.2 + + react-style-singleton@2.2.3(@types/react@19.2.2)(react@19.1.2): + dependencies: + get-nonce: 1.0.1 + react: 19.1.2 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + react@19.1.2: {} + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + resolve-from@4.0.0: {} + + resolve-pkg-maps@1.0.0: {} + + resolve@1.22.11: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + resolve@2.0.0-next.5: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + reusify@1.1.0: {} + + rollup@4.53.3: + dependencies: + '@types/estree': 1.0.8 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.53.3 + '@rollup/rollup-android-arm64': 4.53.3 + '@rollup/rollup-darwin-arm64': 4.53.3 + '@rollup/rollup-darwin-x64': 4.53.3 + '@rollup/rollup-freebsd-arm64': 4.53.3 + '@rollup/rollup-freebsd-x64': 4.53.3 + '@rollup/rollup-linux-arm-gnueabihf': 4.53.3 + '@rollup/rollup-linux-arm-musleabihf': 4.53.3 + '@rollup/rollup-linux-arm64-gnu': 4.53.3 + '@rollup/rollup-linux-arm64-musl': 4.53.3 + '@rollup/rollup-linux-loong64-gnu': 4.53.3 + '@rollup/rollup-linux-ppc64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-gnu': 4.53.3 + '@rollup/rollup-linux-riscv64-musl': 4.53.3 + '@rollup/rollup-linux-s390x-gnu': 4.53.3 + '@rollup/rollup-linux-x64-gnu': 4.53.3 + '@rollup/rollup-linux-x64-musl': 4.53.3 + '@rollup/rollup-openharmony-arm64': 4.53.3 + '@rollup/rollup-win32-arm64-msvc': 4.53.3 + '@rollup/rollup-win32-ia32-msvc': 4.53.3 + '@rollup/rollup-win32-x64-gnu': 4.53.3 + '@rollup/rollup-win32-x64-msvc': 4.53.3 + fsevents: 2.3.3 + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.26.0: {} + + semver@6.3.1: {} + + semver@7.7.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setimmediate@1.0.5: {} + + sharp@0.34.4: + dependencies: + '@img/colour': 1.0.0 + detect-libc: 2.1.2 + semver: 7.7.3 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.34.4 + '@img/sharp-darwin-x64': 0.34.4 + '@img/sharp-libvips-darwin-arm64': 1.2.3 + '@img/sharp-libvips-darwin-x64': 1.2.3 + '@img/sharp-libvips-linux-arm': 1.2.3 + '@img/sharp-libvips-linux-arm64': 1.2.3 + '@img/sharp-libvips-linux-ppc64': 1.2.3 + '@img/sharp-libvips-linux-s390x': 1.2.3 + '@img/sharp-libvips-linux-x64': 1.2.3 + '@img/sharp-libvips-linuxmusl-arm64': 1.2.3 + '@img/sharp-libvips-linuxmusl-x64': 1.2.3 + '@img/sharp-linux-arm': 0.34.4 + '@img/sharp-linux-arm64': 0.34.4 + '@img/sharp-linux-ppc64': 0.34.4 + '@img/sharp-linux-s390x': 0.34.4 + '@img/sharp-linux-x64': 0.34.4 + '@img/sharp-linuxmusl-arm64': 0.34.4 + '@img/sharp-linuxmusl-x64': 0.34.4 + '@img/sharp-wasm32': 0.34.4 + '@img/sharp-win32-arm64': 0.34.4 + '@img/sharp-win32-ia32': 0.34.4 + '@img/sharp-win32-x64': 0.34.4 + optional: true + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + stable-hash@0.0.5: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string.prototype.includes@2.0.1: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + + string.prototype.matchall@4.0.12: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-symbols: 1.1.0 + internal-slot: 1.1.0 + regexp.prototype.flags: 1.5.4 + set-function-name: 2.0.2 + side-channel: 1.1.0 + + string.prototype.repeat@1.0.0: + dependencies: + define-properties: 1.2.1 + es-abstract: 1.24.0 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + styled-jsx@5.1.6(react@19.1.2): + dependencies: + client-only: 0.0.1 + react: 19.1.2 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + swiper@12.0.3: {} + + synckit@0.11.11: + dependencies: + '@pkgr/core': 0.2.9 + + tabbable@6.3.0: {} + + tailwind-merge@3.3.1: {} + + tailwindcss@4.1.16: {} + + tapable@2.3.0: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.15: + dependencies: + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + + tinyrainbow@3.0.3: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + ts-api-utils@2.1.0(typescript@5.9.3): + dependencies: + typescript: 5.9.3 + + tsconfig-paths@3.15.0: + dependencies: + '@types/json5': 0.0.29 + json5: 1.0.2 + minimist: 1.2.8 + strip-bom: 3.0.0 + + tslib@2.8.1: {} + + tw-animate-css@1.4.0: {} + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.9.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + unrs-resolver@1.11.1: + dependencies: + napi-postinstall: 0.3.4 + optionalDependencies: + '@unrs/resolver-binding-android-arm-eabi': 1.11.1 + '@unrs/resolver-binding-android-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-arm64': 1.11.1 + '@unrs/resolver-binding-darwin-x64': 1.11.1 + '@unrs/resolver-binding-freebsd-x64': 1.11.1 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 + '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 + '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 + '@unrs/resolver-binding-linux-x64-musl': 1.11.1 + '@unrs/resolver-binding-wasm32-wasi': 1.11.1 + '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 + '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 + '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + use-callback-ref@1.3.3(@types/react@19.2.2)(react@19.1.2): + dependencies: + react: 19.1.2 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + use-sidecar@1.1.3(@types/react@19.2.2)(react@19.1.2): + dependencies: + detect-node-es: 1.1.0 + react: 19.1.2 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 19.2.2 + + use-sync-external-store@1.6.0(react@19.1.2): + dependencies: + react: 19.1.2 + + util-deprecate@1.0.2: {} + + vaul@1.1.2(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2): + dependencies: + '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.1.2(react@19.1.2))(react@19.1.2) + react: 19.1.2 + react-dom: 19.1.2(react@19.1.2) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + vite@7.2.4(@types/node@20.19.23)(jiti@2.6.1)(lightningcss@1.30.2): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.3) + picomatch: 4.0.3 + postcss: 8.5.6 + rollup: 4.53.3 + tinyglobby: 0.2.15 + optionalDependencies: + '@types/node': 20.19.23 + fsevents: 2.3.3 + jiti: 2.6.1 + lightningcss: 1.30.2 + + vitest@4.0.14(@types/node@20.19.23)(jiti@2.6.1)(lightningcss@1.30.2): + dependencies: + '@vitest/expect': 4.0.14 + '@vitest/mocker': 4.0.14(vite@7.2.4(@types/node@20.19.23)(jiti@2.6.1)(lightningcss@1.30.2)) + '@vitest/pretty-format': 4.0.14 + '@vitest/runner': 4.0.14 + '@vitest/snapshot': 4.0.14 + '@vitest/spy': 4.0.14 + '@vitest/utils': 4.0.14 + es-module-lexer: 1.7.0 + expect-type: 1.2.2 + magic-string: 0.30.21 + obug: 2.1.1 + pathe: 2.0.3 + picomatch: 4.0.3 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.15 + tinyrainbow: 3.0.3 + vite: 7.2.4(@types/node@20.19.23)(jiti@2.6.1)(lightningcss@1.30.2) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 20.19.23 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.2 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + word-wrap@1.2.5: {} + + yocto-queue@0.1.0: {} + + zustand@5.0.8(@types/react@19.2.2)(react@19.1.2)(use-sync-external-store@1.6.0(react@19.1.2)): + optionalDependencies: + '@types/react': 19.2.2 + react: 19.1.2 + use-sync-external-store: 1.6.0(react@19.1.2) diff --git a/postcss.config.mjs b/postcss.config.mjs new file mode 100644 index 00000000..ba720fe5 --- /dev/null +++ b/postcss.config.mjs @@ -0,0 +1,5 @@ +const config = { + plugins: ['@tailwindcss/postcss'], +}; + +export default config; diff --git a/public/assets/album/3Tags_Fill_Album.json b/public/assets/album/3Tags_Fill_Album.json new file mode 100644 index 00000000..2c1985cc --- /dev/null +++ b/public/assets/album/3Tags_Fill_Album.json @@ -0,0 +1 @@ +{"nm":"Comp 1","ddd":0,"h":500,"w":1200,"meta":{"g":"@lottiefiles/toolkit-js 0.66.4","tc":"#ffffff"},"layers":[{"ty":0,"nm":"3개 태그","sr":1.3,"st":-117,"op":248.3,"ip":0,"ln":"253","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"1","ind":1}],"v":"5.7.0","fr":24,"op":246,"ip":0,"assets":[{"nm":"3개 태그","id":"1","fr":24,"layers":[{"ty":0,"nm":"군침돌게_end_2","sr":1,"st":209,"op":428,"ip":293,"ln":"241","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":260.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":271.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":286.999},{"s":[600,110,0],"t":298.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":1},{"ty":0,"nm":"우리만_end_2","sr":1,"st":182,"op":401,"ip":266,"ln":"240","bm":17,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":0.667,"y":0.667},"s":[600,422,0],"t":182},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":233.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":244.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":259.999},{"s":[600,110,0],"t":271.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"8","ind":2},{"ty":0,"nm":"못생기게","sr":1,"st":157,"op":376,"ip":241,"ln":"239","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":208.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":219.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":234.999},{"s":[600,110,0],"t":246.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"9","ind":3},{"ty":0,"nm":"아름다운 풍경_2","sr":1,"st":131,"op":350,"ip":215,"ln":"238","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":182.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":193.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":208.999},{"s":[600,110,0],"t":220.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"12","ind":4},{"ty":0,"nm":"흔들렸지만_2","sr":1,"st":105,"op":324,"ip":189,"ln":"237","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":156.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":167.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":182.999},{"s":[600,110,0],"t":194.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"15","ind":5},{"ty":0,"nm":"예상치 못한_2","sr":1,"st":79,"op":298,"ip":163,"ln":"236","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":130.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":141.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":156.999},{"s":[600,110,0],"t":168.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"18","ind":6},{"ty":0,"nm":"우연히_2","sr":1,"st":53,"op":272,"ip":131,"ln":"235","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":104.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":115.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":130.999},{"s":[600,110,0],"t":142.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"2","ind":7},{"ty":0,"nm":"군침돌게_2","sr":1,"st":26,"op":245,"ip":110,"ln":"234","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":77.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":88.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":103.999},{"s":[600,110,0],"t":115.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":8},{"ty":0,"nm":"우리만","sr":1,"st":0,"op":219,"ip":0,"ln":"233","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":0.667,"y":0.667},"s":[600,422,0],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":51.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":77.999},{"s":[600,110,0],"t":89.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"8","ind":9},{"ty":0,"nm":"군침돌게","sr":1,"st":26,"op":245,"ip":26,"ln":"232","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":77.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":88.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":103.999},{"s":[600,110,0],"t":115.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":10},{"ty":0,"nm":"우연히","sr":1,"st":53,"op":272,"ip":53,"ln":"231","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":104.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":115.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":130.999},{"s":[600,110,0],"t":142.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"2","ind":11},{"ty":0,"nm":"예상치 못한","sr":1,"st":79,"op":298,"ip":79,"ln":"230","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":130.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":141.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":156.999},{"s":[600,110,0],"t":168.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"18","ind":12},{"ty":0,"nm":"흔들렸지만","sr":1,"st":105,"op":324,"ip":105,"ln":"229","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":156.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":167.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":182.999},{"s":[600,110,0],"t":194.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"15","ind":13},{"ty":0,"nm":"아름다운 풍경","sr":1,"st":131,"op":350,"ip":131,"ln":"228","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":182.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":193.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":208.999},{"s":[600,110,0],"t":220.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"12","ind":14},{"ty":0,"nm":"못생기게","sr":1,"st":157,"op":376,"ip":157,"ln":"227","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":208.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":219.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":234.999},{"s":[600,110,0],"t":246.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"9","ind":15},{"ty":0,"nm":"우리만_end","sr":1,"st":182,"op":401,"ip":206,"ln":"226","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":0.667,"y":0.667},"s":[600,422,0],"t":182},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":233.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":244.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":259.999},{"s":[600,110,0],"t":271.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"8","ind":16},{"ty":0,"nm":"군침돌게_end","sr":1,"st":209,"op":428,"ip":209,"ln":"225","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":260.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":271.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":286.999},{"s":[600,110,0],"t":298.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":17},{"ty":0,"nm":"우연히_end","sr":1,"st":236,"op":455,"ip":236,"ln":"224","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":287.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":298.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":313.999},{"s":[600,110,0],"t":325.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"2","ind":18}]},{"nm":"우연히","id":"2","fr":24,"layers":[{"ty":2,"nm":"Element-2.png","sr":1,"st":0,"op":219,"ip":0,"ln":"93","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"3","ind":1},{"ty":2,"nm":"Element_S-2.png","sr":1,"st":0,"op":219,"ip":0,"ln":"92","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"4","ind":2}]},{"id":"3","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"4","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"군침돌게","id":"5","fr":24,"layers":[{"ty":2,"nm":"군침돌게","sr":1,"st":0,"op":219,"ip":0,"ln":"68","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"6","ind":1},{"ty":2,"nm":"군침돌게_s","sr":1,"st":0,"op":219,"ip":0,"ln":"67","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"7","ind":2}]},{"id":"6","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"7","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"우리만","id":"8","fr":24,"layers":[{"ty":2,"nm":"우리만","sr":1,"st":0,"op":219,"ip":0,"ln":"50","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100.862],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,150],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"21","ind":1},{"ty":2,"nm":"우리만_s","sr":1,"st":0,"op":219,"ip":0,"ln":"49","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100.385],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,98.182],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"22","ind":2}]},{"nm":"못생기게","id":"9","fr":24,"layers":[{"ty":2,"nm":"Element-6.png","sr":1,"st":0,"op":219,"ip":0,"ln":"195","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"10","ind":1},{"ty":2,"nm":"Element_S-6.png","sr":1,"st":0,"op":219,"ip":0,"ln":"194","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"11","ind":2}]},{"id":"10","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"11","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"아름다운 풍경","id":"12","fr":24,"layers":[{"ty":2,"nm":"Element-5.png","sr":1,"st":0,"op":219,"ip":0,"ln":"168","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"13","ind":1},{"ty":2,"nm":"Element_S-5.png","sr":1,"st":0,"op":219,"ip":0,"ln":"167","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"14","ind":2}]},{"id":"13","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"14","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"흔들렸지만","id":"15","fr":24,"layers":[{"ty":2,"nm":"Element-4.png","sr":1,"st":0,"op":219,"ip":0,"ln":"142","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"16","ind":1},{"ty":2,"nm":"Element_S-4.png","sr":1,"st":0,"op":219,"ip":0,"ln":"141","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"17","ind":2}]},{"id":"16","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"17","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"예상치 못한","id":"18","fr":24,"layers":[{"ty":2,"nm":"Element-3.png","sr":1,"st":0,"op":219,"ip":0,"ln":"117","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"19","ind":1},{"ty":2,"nm":"Element_S-3.png","sr":1,"st":0,"op":219,"ip":0,"ln":"116","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"20","ind":2}]},{"id":"19","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"20","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"id":"21","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"22","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""}]} \ No newline at end of file diff --git a/public/assets/album/CheeseCart_Loading.json b/public/assets/album/CheeseCart_Loading.json new file mode 100644 index 00000000..3143d18d --- /dev/null +++ b/public/assets/album/CheeseCart_Loading.json @@ -0,0 +1 @@ +{"nm":"cheeseCart_Loading","ddd":0,"h":360,"w":1200,"meta":{"g":"@lottiefiles/toolkit-js 0.66.4","tc":"#ffffff"},"layers":[{"ty":0,"nm":"치즈카트","sr":1,"st":0,"op":106,"ip":0,"ln":"40","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,180]},"s":{"a":0,"k":[100,100]},"p":{"s":true,"x":{"a":1,"k":[{"o":{"x":0.794,"y":-0.372},"i":{"x":0.339,"y":1.281},"s":[600],"t":39},{"s":[-330.75],"t":69.999}]},"y":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[180],"t":39},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[180],"t":69.999},{"s":[180],"t":76.999}]},"z":{"a":0,"k":0}},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":360,"refId":"1","ind":1}],"v":"5.7.0","fr":24,"op":75,"ip":0,"assets":[{"nm":"치즈카트","id":"1","fr":24,"layers":[{"ty":2,"nm":"치즈_3.png","sr":1,"st":19,"op":125,"ip":19,"ln":"29","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[86,18]},"s":{"a":0,"k":[154.199,154.199,89.535]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,-209.5,0],"t":32},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,53.5,0],"t":36},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,49.7,0],"t":37},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,50.402,0],"t":38},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,72.5,0],"t":40},{"s":[579.5,84.5,0],"t":42}]},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[23],"t":32},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[7.667],"t":35},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-7.167],"t":36},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[7.267],"t":37},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[12.45],"t":38},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-3],"t":40},{"s":[0],"t":42}]},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"refId":"2","ind":1},{"ty":2,"nm":"치즈_2.png","sr":1,"st":18,"op":124,"ip":18,"ln":"28","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[85,18]},"s":{"a":0,"k":[165.425,165.425,93.051]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[545.5,-146.5,0],"t":29},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[545.5,129.5,0],"t":32},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[545.5,127.5,0],"t":33},{"s":[545.5,147.5,0],"t":37}]},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-14],"t":32},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[6],"t":35},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-7],"t":36},{"s":[0],"t":37}]},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"refId":"3","ind":2},{"ty":2,"nm":"치즈_1.png","sr":1,"st":15,"op":121,"ip":15,"ln":"27","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[86,18]},"s":{"a":0,"k":[154.199,154.199,89.535]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,-84.5,0],"t":22},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,191.5,0],"t":28},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,211.5,0],"t":29},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[579.5,191.5,0],"t":30},{"s":[579.5,211.5,0],"t":34}]},"r":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[25],"t":25},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":29},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[-9],"t":30},{"s":[0],"t":34}]},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"refId":"2","ind":3},{"ty":2,"nm":"카트.png","sr":1,"st":0,"op":106,"ip":0,"ln":"26","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[111,69]},"s":{"a":0,"k":[156.796,158.911,100]},"p":{"s":true,"x":{"a":1,"k":[{"o":{"x":0.933,"y":0},"i":{"x":0.157,"y":1},"s":[1532.75],"t":4},{"s":[602.833],"t":22}]},"y":{"a":1,"k":[{"o":{"x":0.167,"y":0.167},"i":{"x":0.833,"y":0.833},"s":[192],"t":4},{"s":[192],"t":22}]},"z":{"a":0,"k":0}},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"refId":"4","ind":4}]},{"id":"2","e":1,"w":172,"h":36,"p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKwAAAAkCAYAAAAU0tFtAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAASbSURBVHgB7ZxfctNWFMa/a8khQP44DaRhSqbuMH1vVtB2Be0Sygogz2WKM4X37qB0B7CCwgrgnRkw42EIBIgTQhInssX9JNtxElm2b65sSZzfjCe2pSQz0qdzz797FGLwX1ZKcL1b+u0vUH4ZvipDyB6tFrDxHEYs/wgUCkgWVddqewalHsFxHqprlWrfM6O+1EIta6H+CwpVyAfva8DhHkbm2xuA42KsKPVAC3c9SrhnHh2/9tctuM2nELHmi4VlM0upkrauEfj+H/C8p/6rP2+fPnTCwvq1O3f1jwqSgMtSY7f9Xx3A1U+tewGCRQ739esAOGroa1sELlwCpi4eH9/dAnbeDf3nUNT352oZE6aiVu6tdz50BRtYVrT+gW28I6C+Eb0cOe2LOrMYXmDBnE8f9Ov92e+n9PW9snL8eRTRlq4Bl+YwcVr+mvr+fqDNQLChz0o3wC/BJkf6af9QC61rHBTu7JV0XJys8vYF0DyKPsZrO7t4/JnnUeAHn6LvDV2HuSV9P+aREup6RV6lTxt608XmXe032BUrLeswYiW8gPU3+nlpApcXIBjAa9ePzx9PCpYGoqR9WiyHK1+v0OmmFaeRMkrap2US4FfVzgi8hE0o0s1q/ye+H3yyl26MIY2SQwZlAZbK2Y8ZPHehoK3r77DN563RxUoodJPUiwDMX40/3moi87je7YJ2BX6Dbfa2YYxYVzO4jNPvjIIuAIOv7POz9mHVT9oBgjWYWjGxrh0cyRYYM6P9/4szYUDFGIL3gUKdW0QuUKrsWs8MMA9oCqNSEez56AZUOcT3y/bXX9MlPUht5cQSCIlhX7DTs2ainV8S6yoMJBkL28/573f+N99poc9AEAaRTBsOfVGWXLc24tNUDAjYlHHasvYGbjxWcKV0KwQk1zdGobGGTeE1tGi9Q/2+nQtk40vQmHEq1cJS7vZmtMh5LoMJEe5XTfKNjhTuMDXpQaVcinjrdRq6h4QJkp4sPTu6hmmSEb5qslNWokswP0IwJ+SSMe99iIH+Lku6ezvaPTgIrS0zCO506FJI66GANAmWBMJMTQ+mMCpNL9xSk2A/SLoEK2QProTcwdDbDM5GHPY1J7AqSmuUcD4oVrpyvQEz05NsyD/YhW1EsML5OGz0P7Y9wobHISlA+VUIgilxhZxO0cge9QJ8VYUgmHJ5QHcqAzF7PKNL8ASCYApL7HGNS67FuF75jwrwXPuzCMZJsA9sH9jfCV98P8xOXcEenF8QtduZWQKbW3Oc4sNwLkHtzv/I2miiuEYZwnyuDOgYLzQUnaIP53HZ3C6u1AN1/e+bYZbAc2+GE+QyAqeXbL6Kb11kquXdi7ByJowHFgym2i6C1dkGWpuOE4wrCgSrfqhUobCOLEAhjjIfivnAhmwdzzSq1Z1k2M3DanNLXzb9ot01WAi4i1TIKuvq+v1unHWicKBW7lW0r7CWavfAM9iV651jJ68wIbQGlb8WaLKHM5WuwNJ6zip8/AdBmAyP4TqrvZa1g4r7rWDuFkcZBdNhgoEbdmcYmGAySZoZg7zu1c8DrLaGBawnTLPqmKrvCv8FmyNx5ySbZy8AAAAASUVORK5CYII=","u":""},{"id":"3","e":1,"w":170,"h":36,"p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAKoAAAAkCAYAAAAZzKEqAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAVLSURBVHgB7ZxPbxNHGMaftZcmaUJjlLSkBbUrUYlDD7TqoYceWg5V1VPbT9BWSiVu5BvE/gTNsQek5iPAqZVoBT32gIBDD0ggDOJPgCAc4UBCNlnm8WTIxtm/sw7MrucnWfba40Treead933nnXGQQHBhrgG/e1q8/BpO4CFwPJSBiRngveO5voLH14GVu7C8Rhx0EOCKeHUOrnvWOfl7O75pBMGFUx58/w9QoGVktAF8cCLXV9BdAh5eQ+WoucDBw8CY+E3empDXWz6wuiwG5w0YheMsol5vRQm21v9G8Pevp7HpX0ZZRUrWOrIz8rCZs30ZmDwCfPgFMPUx8PY04I5KofJ58qh8Nokg+FkYyMvBX7Nz/R/tEmrwz+y8aLwgzHEDZWc5p7VYfYxKQfeHAqUwy0UDdfwWnBdaDPFKqD1LuoUmsmL6D8Cp/MmtbG2fLkkrXCUOjCR/7q/Jh7k0w5a156P2fFJO92mWlKP0ncM7vg5hB9O3M/WmD3ni8VH85+td4MH/pndafjit00+Pm94fXZMD1Gw6Isj6jD6rtKibm/OpIp06JiNpBipha8pr+kH0gUzkSRu4cwl4trz7fQqTFvfupeqJlPCe7l3dK0b67sxumC9S0tgO6kXSSUb4N1O/8v4JGTnGwSiSlsl0aGHYWVsVDJ6S4CxIKOCy3bvrH3KFSH/I1JhTBafQ8em9/qkapWUgbD15H7ynEdGJG2syXVNVAb/oorS8cOeouO8zNWYHU6x8hH1U853yeChSpmkIXTnlDljMooavqLZPkZe40Unx0lcdGQfqrsxNPl/Z6x+aQv/MoKZHi1k4jseeKp4zZQdPH5OBVT+0WLRUzAyYlgLiIDo4s3M9bH5rWQgCr3gylKsfTCwnoVIljMBNmlq725Evc44b6zvXFuMoJlT6eMxTZm7vSev69AGMwYqzFNSgS16RKmh9TVtjthiPnlApNB2R9v6jKxcPLJYc6Al1tGD8FZWLtVgS0BPq2CQKwxpJiyUjb86sjdicZalhSnLmE+kGvobCJD2LOojlOBtQlRf2nRIp0dlRkRM9oTK9VDQ5XsWK+mGBGZ9+Q6N2D+wTen+ZImXivkj0/qxiFfXDRJTbxmXysPGilR2fkkvpStQb2zl0jRVK/SGwcmen+igv9GWeV6yifpjonw2Vj0o9cKWSy+ZR1pV65ZK12liYw6ctZqu5JMpRlMeyqoLeKhYrDwv3r8riI4qR/UihhoOrNJiepFXOoYPiTgUtK0cILWu4wKMfVbPK9rb4o/yEK+L6g6sssO27x6XoszSHE7QLHyyhalVpzumbsMzvwKicIvx1uS+JmQIr0GoSFVxlgTtGmE9Pr/3ouEKkbfHCwyCgEDnSTK0/tewPSTNpGtwwmi7UK0xP/QuLRZei+fCxRpa01rkaXH8BFosug9gsmCZ23z1bc04uMk90ERaLLqsFXb0ki+o4i853al+/7/4Cbva3WHTg4k+RdGP8knwHG/UWX/SESsWKpxYsg0ElvnlgBx+MiouWRpoMRbqkedpMsuvQ2tbm7mMng/OzTfE0D4s+FOnRz6P9rt527LZZW3EGSdoxQlHwxJZHEcd9bgmRfnumqS73nI8qxMqDqSjWCpuAfYSrdOqsgDi46GHa2aSDhLs/mB9NDZIiVynpgracb87sCvKjD/L985SHut8Un/4ESz54DlcWi2LajtxBw9+A7k7jSPR5CdE1rBcZL6npPkykUBU9wbq9I394mgoPqrBWNglO+96X2dqyg27/h6GAvwvFqgYwRcr7l6uibTCX351YcH5ciA3oXwL3zc4P+BajXgAAAABJRU5ErkJggg==","u":""},{"id":"4","e":1,"w":222,"h":138,"p":"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAN4AAACKCAYAAADFePRWAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAASBSURBVHgB7dxNchNHGIfxt8feQLFQbqCcIM4JcoRwA3vMIiXHVbZOYOsGWsQWLMLYN0hukJwA3wAdQQtKRYHVL9MCg4tPDWj6PyU/v51cBUX55dF8dU8wYMNVVdWbv7Qjd9sz875lFa5CsKvCbDQYlNP3PzVgg50/ebq7uLZx/Z+/Z1qzOvrR4cGjcfpAeNhYZ4+rI48+tg4ptmzv4I/9S8LDRppMqv519GcdONJ9bPb6Xvi5MGADRbOTDkaX9Lbn8ZjwsJHcfcc6KoSwS3jYVJ0Nr9YnPECA8ID8rggPyKy+xiM8ICd3m6VVLIQHZBSCL5eOER6QQTrS1dGVN0vGtg1Ai8LUPV4+uF+My3J/dvNTwgNuOTzYz7KMklNNQIDwAAHCAwQIDxAgPECA8AABwgMECA8QIDxAgPAAAcIDBAgPECA8QIDwAAHCAwQIDxAgPECA8AABwgMECA8QIDxAgPAAAcIDBAgPECA8QIDwAAHCAwQIDxAgPECA8AABwgMECA8QIDxAgPAAAcIDBAgPECA8QIDwAAHCAwQIDxAgPECA8AABwgMECA8QIDxAgPAAAcIDBAgPECA8QIDwAAHCAwQIDxAgPECA8AABwgMECA8QIDxAgPAAAcIDBAgPECA8QIDwAAHCAwQIDxAgPECA8AABwgMECA8QIDxAgPAAAcIDBAgPECA8QIDwAAHCAwQIDxAgPECA8AABwgMECA8QIDxAgPAAAcIDBAgPECA8QIDwAAHCAwS2b3+oqqo3f2lH7rZn5n0D0Ir34Z0/ebr7Yu7jEKxnAFq1PNU8e1wdxYVdEB2QRzGZVP248FMDkE0RzU440gF5Fe6+YwCyStd4hAdkxnM8QKAOL0wNQHJlmdTXePHSAFgIIV9416+KsbnNDLjDvG6gPv0bWSbFcFjOQhGGBtxhIfhoMCinlsny5sqfg/Kibn7IkQ93TTrS1dGVhwePxpZRuP0hrWJZeFrFEn6p/0k8ZsAGC9N0f+PB/WJclmX2A04wsbfrRP30O1bP1L8sH+X+psJqmOvXScM7m1Qn7j+2TrS+E3VanypnuyjGtzHXb5OFl74RPfqavtV8yJGvG5jraiThpWvJ6+jP1rg4e7YVwq8570rhU8x1dZIlYy3siOgtzCqDFHNdXfYj3rs7p8+tBa/vhZ+GgjtUYK5NZT/iLTw+tJZsz+OxQYK5NpP/VDMUv1tLQlH8ZtBgro3kDy+2+GDeeTOaDHNtRHDEa/U1E32DBnNthI2wgIAgvFY33nJHU4a5NqE41ZxaWzJuZMRHmGsj2cPzGP+3tnj81yDBXJvJHt5yx3tLtkLxj0GCuTaTPby0470+dfjP1iyEcMFaTR3m2ozkruaWWbnO3e6535eBz2Ouq5OEt/wGC762X2ju92Xg85jr6mTP8dI+K/cfH1L6O9iL1x3MdTXyVz/8df73sXk4abry4d1Laoiuo5jr18nDSz68ZMl2V/oD9UV8up7g9LLbmOuXdSK8G28HFR+mle4efefDpsowTQ9o07Mi1Vuh8P2Y66feAKwqogWUGSX3AAAAAElFTkSuQmCC","u":""}]} \ No newline at end of file diff --git a/public/assets/album/bg-album-default.png b/public/assets/album/bg-album-default.png new file mode 100644 index 00000000..d482c111 Binary files /dev/null and b/public/assets/album/bg-album-default.png differ diff --git a/public/assets/album/bg-album-entry.png b/public/assets/album/bg-album-entry.png new file mode 100644 index 00000000..02666a1a Binary files /dev/null and b/public/assets/album/bg-album-entry.png differ diff --git a/public/assets/album/letter-full-size.svg b/public/assets/album/letter-full-size.svg new file mode 100644 index 00000000..3cfea587 --- /dev/null +++ b/public/assets/album/letter-full-size.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/album/no-album-icon.svg b/public/assets/album/no-album-icon.svg new file mode 100644 index 00000000..f5d0af59 --- /dev/null +++ b/public/assets/album/no-album-icon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/album/test-lottie.svg b/public/assets/album/test-lottie.svg new file mode 100644 index 00000000..5c5ec880 --- /dev/null +++ b/public/assets/album/test-lottie.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/public/assets/login/cheese-icon.svg b/public/assets/login/cheese-icon.svg new file mode 100644 index 00000000..8382e81f --- /dev/null +++ b/public/assets/login/cheese-icon.svg @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/login/cheese-logo.svg b/public/assets/login/cheese-logo.svg new file mode 100644 index 00000000..7f62ec5b --- /dev/null +++ b/public/assets/login/cheese-logo.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/public/assets/login/kakao-logo.svg b/public/assets/login/kakao-logo.svg new file mode 100644 index 00000000..52c7019c --- /dev/null +++ b/public/assets/login/kakao-logo.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/login/reverse-triangle-black.svg b/public/assets/login/reverse-triangle-black.svg new file mode 100644 index 00000000..a201d2a9 --- /dev/null +++ b/public/assets/login/reverse-triangle-black.svg @@ -0,0 +1,3 @@ + + + diff --git a/public/assets/login/triangle-polygon.svg b/public/assets/login/triangle-polygon.svg new file mode 100644 index 00000000..5e89dff8 --- /dev/null +++ b/public/assets/login/triangle-polygon.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/public/assets/og/og_kakao.png b/public/assets/og/og_kakao.png new file mode 100644 index 00000000..754121d6 Binary files /dev/null and b/public/assets/og/og_kakao.png differ diff --git a/public/assets/onboarding/congratulation.svg b/public/assets/onboarding/congratulation.svg new file mode 100644 index 00000000..7a3aa5d7 --- /dev/null +++ b/public/assets/onboarding/congratulation.svg @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/rending/blur.svg b/public/assets/rending/blur.svg new file mode 100644 index 00000000..f426253c --- /dev/null +++ b/public/assets/rending/blur.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/public/assets/rending/box.png b/public/assets/rending/box.png new file mode 100644 index 00000000..f8b73ab4 Binary files /dev/null and b/public/assets/rending/box.png differ diff --git a/public/assets/rending/box.svg b/public/assets/rending/box.svg new file mode 100644 index 00000000..32e0201c --- /dev/null +++ b/public/assets/rending/box.svg @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/rending/first.svg b/public/assets/rending/first.svg new file mode 100644 index 00000000..8f319d6a --- /dev/null +++ b/public/assets/rending/first.svg @@ -0,0 +1,166 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/rending/phone.png b/public/assets/rending/phone.png new file mode 100644 index 00000000..dffb3d8b Binary files /dev/null and b/public/assets/rending/phone.png differ diff --git a/public/assets/rending/second.svg b/public/assets/rending/second.svg new file mode 100644 index 00000000..14cef82c --- /dev/null +++ b/public/assets/rending/second.svg @@ -0,0 +1,134 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/rending/swipe/1.png b/public/assets/rending/swipe/1.png new file mode 100644 index 00000000..abb58752 Binary files /dev/null and b/public/assets/rending/swipe/1.png differ diff --git a/public/assets/rending/swipe/2.png b/public/assets/rending/swipe/2.png new file mode 100644 index 00000000..e6cf72e0 Binary files /dev/null and b/public/assets/rending/swipe/2.png differ diff --git a/public/assets/rending/swipe/3.png b/public/assets/rending/swipe/3.png new file mode 100644 index 00000000..72ba561c Binary files /dev/null and b/public/assets/rending/swipe/3.png differ diff --git a/public/assets/rending/swipe/4.png b/public/assets/rending/swipe/4.png new file mode 100644 index 00000000..8a3849d6 Binary files /dev/null and b/public/assets/rending/swipe/4.png differ diff --git a/public/assets/rending/text-group.svg b/public/assets/rending/text-group.svg new file mode 100644 index 00000000..df172aaa --- /dev/null +++ b/public/assets/rending/text-group.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/public/assets/rending/third.svg b/public/assets/rending/third.svg new file mode 100644 index 00000000..629412d0 --- /dev/null +++ b/public/assets/rending/third.svg @@ -0,0 +1,211 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/assets/upload/3Tags_Fill Album.json b/public/assets/upload/3Tags_Fill Album.json new file mode 100644 index 00000000..2c1985cc --- /dev/null +++ b/public/assets/upload/3Tags_Fill Album.json @@ -0,0 +1 @@ +{"nm":"Comp 1","ddd":0,"h":500,"w":1200,"meta":{"g":"@lottiefiles/toolkit-js 0.66.4","tc":"#ffffff"},"layers":[{"ty":0,"nm":"3개 태그","sr":1.3,"st":-117,"op":248.3,"ip":0,"ln":"253","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"1","ind":1}],"v":"5.7.0","fr":24,"op":246,"ip":0,"assets":[{"nm":"3개 태그","id":"1","fr":24,"layers":[{"ty":0,"nm":"군침돌게_end_2","sr":1,"st":209,"op":428,"ip":293,"ln":"241","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":260.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":271.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":286.999},{"s":[600,110,0],"t":298.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":1},{"ty":0,"nm":"우리만_end_2","sr":1,"st":182,"op":401,"ip":266,"ln":"240","bm":17,"hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":0.667,"y":0.667},"s":[600,422,0],"t":182},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":233.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":244.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":259.999},{"s":[600,110,0],"t":271.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"8","ind":2},{"ty":0,"nm":"못생기게","sr":1,"st":157,"op":376,"ip":241,"ln":"239","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":208.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":219.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":234.999},{"s":[600,110,0],"t":246.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"9","ind":3},{"ty":0,"nm":"아름다운 풍경_2","sr":1,"st":131,"op":350,"ip":215,"ln":"238","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":182.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":193.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":208.999},{"s":[600,110,0],"t":220.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"12","ind":4},{"ty":0,"nm":"흔들렸지만_2","sr":1,"st":105,"op":324,"ip":189,"ln":"237","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":156.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":167.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":182.999},{"s":[600,110,0],"t":194.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"15","ind":5},{"ty":0,"nm":"예상치 못한_2","sr":1,"st":79,"op":298,"ip":163,"ln":"236","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":130.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":141.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":156.999},{"s":[600,110,0],"t":168.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"18","ind":6},{"ty":0,"nm":"우연히_2","sr":1,"st":53,"op":272,"ip":131,"ln":"235","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":104.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":115.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":130.999},{"s":[600,110,0],"t":142.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"2","ind":7},{"ty":0,"nm":"군침돌게_2","sr":1,"st":26,"op":245,"ip":110,"ln":"234","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":77.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":88.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":103.999},{"s":[600,110,0],"t":115.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":8},{"ty":0,"nm":"우리만","sr":1,"st":0,"op":219,"ip":0,"ln":"233","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":0.667,"y":0.667},"s":[600,422,0],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":51.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":77.999},{"s":[600,110,0],"t":89.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"8","ind":9},{"ty":0,"nm":"군침돌게","sr":1,"st":26,"op":245,"ip":26,"ln":"232","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":77.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":88.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":103.999},{"s":[600,110,0],"t":115.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":10},{"ty":0,"nm":"우연히","sr":1,"st":53,"op":272,"ip":53,"ln":"231","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":104.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":115.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":130.999},{"s":[600,110,0],"t":142.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"2","ind":11},{"ty":0,"nm":"예상치 못한","sr":1,"st":79,"op":298,"ip":79,"ln":"230","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":130.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":141.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":156.999},{"s":[600,110,0],"t":168.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"18","ind":12},{"ty":0,"nm":"흔들렸지만","sr":1,"st":105,"op":324,"ip":105,"ln":"229","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":156.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":167.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":182.999},{"s":[600,110,0],"t":194.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"15","ind":13},{"ty":0,"nm":"아름다운 풍경","sr":1,"st":131,"op":350,"ip":131,"ln":"228","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":182.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":193.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":208.999},{"s":[600,110,0],"t":220.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"12","ind":14},{"ty":0,"nm":"못생기게","sr":1,"st":157,"op":376,"ip":157,"ln":"227","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":208.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":219.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":234.999},{"s":[600,110,0],"t":246.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"9","ind":15},{"ty":0,"nm":"우리만_end","sr":1,"st":182,"op":401,"ip":206,"ln":"226","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0,"y":0},"i":{"x":0.667,"y":0.667},"s":[600,422,0],"t":182},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":233.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":244.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":259.999},{"s":[600,110,0],"t":271.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"8","ind":16},{"ty":0,"nm":"군침돌게_end","sr":1,"st":209,"op":428,"ip":209,"ln":"225","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":260.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":271.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":286.999},{"s":[600,110,0],"t":298.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"5","ind":17},{"ty":0,"nm":"우연히_end","sr":1,"st":236,"op":455,"ip":236,"ln":"224","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[600,250]},"s":{"a":0,"k":[100,100]},"p":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,422,0],"t":287.999},{"o":{"x":0.333,"y":0.333},"i":{"x":0.667,"y":0.667},"s":[600,266,0],"t":298.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[600,266,0],"t":313.999},{"s":[600,110,0],"t":325.999}]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":0,"k":100}},"w":1200,"h":500,"refId":"2","ind":18}]},{"nm":"우연히","id":"2","fr":24,"layers":[{"ty":2,"nm":"Element-2.png","sr":1,"st":0,"op":219,"ip":0,"ln":"93","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"3","ind":1},{"ty":2,"nm":"Element_S-2.png","sr":1,"st":0,"op":219,"ip":0,"ln":"92","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"4","ind":2}]},{"id":"3","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"4","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"군침돌게","id":"5","fr":24,"layers":[{"ty":2,"nm":"군침돌게","sr":1,"st":0,"op":219,"ip":0,"ln":"68","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"6","ind":1},{"ty":2,"nm":"군침돌게_s","sr":1,"st":0,"op":219,"ip":0,"ln":"67","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"7","ind":2}]},{"id":"6","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"7","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"우리만","id":"8","fr":24,"layers":[{"ty":2,"nm":"우리만","sr":1,"st":0,"op":219,"ip":0,"ln":"50","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100.862],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,150],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"21","ind":1},{"ty":2,"nm":"우리만_s","sr":1,"st":0,"op":219,"ip":0,"ln":"49","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100.385],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":0},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,98.182],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":0},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"22","ind":2}]},{"nm":"못생기게","id":"9","fr":24,"layers":[{"ty":2,"nm":"Element-6.png","sr":1,"st":0,"op":219,"ip":0,"ln":"195","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"10","ind":1},{"ty":2,"nm":"Element_S-6.png","sr":1,"st":0,"op":219,"ip":0,"ln":"194","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"11","ind":2}]},{"id":"10","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"11","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"아름다운 풍경","id":"12","fr":24,"layers":[{"ty":2,"nm":"Element-5.png","sr":1,"st":0,"op":219,"ip":0,"ln":"168","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"13","ind":1},{"ty":2,"nm":"Element_S-5.png","sr":1,"st":0,"op":219,"ip":0,"ln":"167","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"14","ind":2}]},{"id":"13","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"14","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"흔들렸지만","id":"15","fr":24,"layers":[{"ty":2,"nm":"Element-4.png","sr":1,"st":0,"op":219,"ip":0,"ln":"142","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"16","ind":1},{"ty":2,"nm":"Element_S-4.png","sr":1,"st":0,"op":219,"ip":0,"ln":"141","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"17","ind":2}]},{"id":"16","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"17","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"nm":"예상치 못한","id":"18","fr":24,"layers":[{"ty":2,"nm":"Element-3.png","sr":1,"st":0,"op":219,"ip":0,"ln":"117","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[300,44]},"s":{"a":1,"k":[{"s":[117,117,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[150,150,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[117,117,100],"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[0],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":53.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":82.999},{"s":[0],"t":89.999}]}},"refId":"19","ind":1},{"ty":2,"nm":"Element_S-3.png","sr":1,"st":0,"op":219,"ip":0,"ln":"116","hasMask":false,"ao":0,"ks":{"a":{"a":0,"k":[240,36]},"s":{"a":1,"k":[{"s":[145,145,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":48},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":62.999},{"s":[186,186,100],"i":{"x":[0.667,0.667,0.667],"y":[1,1,1]},"o":{"x":[0.333,0.333,0.333],"y":[0,0,0]},"t":77.999},{"s":[145,145,100],"i":{"x":[0.833,0.833,0.833],"y":[1,1,1]},"o":{"x":[0.167,0.167,0.167],"y":[0,0,0]},"t":89.999}]},"p":{"a":0,"k":[600,250]},"r":{"a":0,"k":0},"sa":{"a":0,"k":0},"o":{"a":1,"k":[{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":48},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":62.999},{"o":{"x":0.333,"y":0},"i":{"x":0.667,"y":1},"s":[100],"t":77.999},{"s":[100],"t":89.999}]}},"refId":"20","ind":2}]},{"id":"19","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"20","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""},{"id":"21","e":1,"w":600,"h":88,"p":"data:image/png;base64,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","u":""},{"id":"22","e":1,"w":480,"h":72,"p":"data:image/png;base64,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","u":""}]} \ No newline at end of file diff --git a/public/background/bg_qrcode.svg b/public/background/bg_qrcode.svg new file mode 100644 index 00000000..d5ebb9bb --- /dev/null +++ b/public/background/bg_qrcode.svg @@ -0,0 +1,88 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 00000000..39069967 Binary files /dev/null and b/public/favicon.ico differ diff --git a/public/file.svg b/public/file.svg new file mode 100644 index 00000000..004145cd --- /dev/null +++ b/public/file.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/fonts/PretendardVariable.woff2 b/public/fonts/PretendardVariable.woff2 new file mode 100644 index 00000000..49c54b51 Binary files /dev/null and b/public/fonts/PretendardVariable.woff2 differ diff --git a/public/globe.svg b/public/globe.svg new file mode 100644 index 00000000..567f17b0 --- /dev/null +++ b/public/globe.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/icon/error-image.svg b/public/icon/error-image.svg new file mode 100644 index 00000000..276b6ed8 --- /dev/null +++ b/public/icon/error-image.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + diff --git a/public/icon/icon_kakao.png b/public/icon/icon_kakao.png new file mode 100644 index 00000000..ac83a157 Binary files /dev/null and b/public/icon/icon_kakao.png differ diff --git a/public/next.svg b/public/next.svg new file mode 100644 index 00000000..5174b28c --- /dev/null +++ b/public/next.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/og/default_og.png b/public/og/default_og.png new file mode 100644 index 00000000..9e36b57e Binary files /dev/null and b/public/og/default_og.png differ diff --git a/public/og/invite_og.png b/public/og/invite_og.png new file mode 100644 index 00000000..ffe2452b Binary files /dev/null and b/public/og/invite_og.png differ diff --git a/public/og/toMaker_og.png b/public/og/toMaker_og.png new file mode 100644 index 00000000..51868fc0 Binary files /dev/null and b/public/og/toMaker_og.png differ diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 00000000..28a33c28 --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,10 @@ +# * +User-agent: * +Allow: / + +# Disallow private pages +Disallow: /mypage/ +Disallow: /onboarding/ +Disallow: /album/detail/ +# Sitemap +Sitemap: https://say-cheese.me/sitemap.xml diff --git a/public/test/4cut-example.png b/public/test/4cut-example.png new file mode 100644 index 00000000..ed2f9e88 Binary files /dev/null and b/public/test/4cut-example.png differ diff --git a/public/ut/1.jpg b/public/ut/1.jpg new file mode 100644 index 00000000..a4b87ece Binary files /dev/null and b/public/ut/1.jpg differ diff --git a/public/ut/10.jpg b/public/ut/10.jpg new file mode 100644 index 00000000..43e7e8a7 Binary files /dev/null and b/public/ut/10.jpg differ diff --git a/public/ut/11.jpg b/public/ut/11.jpg new file mode 100644 index 00000000..35af5229 Binary files /dev/null and b/public/ut/11.jpg differ diff --git "a/public/ut/1\354\243\274\354\260\250_1.jpg" "b/public/ut/1\354\243\274\354\260\250_1.jpg" new file mode 100644 index 00000000..f67e6770 Binary files /dev/null and "b/public/ut/1\354\243\274\354\260\250_1.jpg" differ diff --git "a/public/ut/1\354\243\274\354\260\250_2.jpg" "b/public/ut/1\354\243\274\354\260\250_2.jpg" new file mode 100644 index 00000000..8fe2c889 Binary files /dev/null and "b/public/ut/1\354\243\274\354\260\250_2.jpg" differ diff --git "a/public/ut/1\354\243\274\354\260\250_3.jpg" "b/public/ut/1\354\243\274\354\260\250_3.jpg" new file mode 100644 index 00000000..f10fa012 Binary files /dev/null and "b/public/ut/1\354\243\274\354\260\250_3.jpg" differ diff --git "a/public/ut/1\354\243\274\354\260\250_4.jpg" "b/public/ut/1\354\243\274\354\260\250_4.jpg" new file mode 100644 index 00000000..4a29deb3 Binary files /dev/null and "b/public/ut/1\354\243\274\354\260\250_4.jpg" differ diff --git a/public/ut/2.jpg b/public/ut/2.jpg new file mode 100644 index 00000000..6cafbd9e Binary files /dev/null and b/public/ut/2.jpg differ diff --git "a/public/ut/2\354\243\274\354\260\250_1.jpg" "b/public/ut/2\354\243\274\354\260\250_1.jpg" new file mode 100644 index 00000000..ab8bfe54 Binary files /dev/null and "b/public/ut/2\354\243\274\354\260\250_1.jpg" differ diff --git "a/public/ut/2\354\243\274\354\260\250_2.jpg" "b/public/ut/2\354\243\274\354\260\250_2.jpg" new file mode 100644 index 00000000..01fe3985 Binary files /dev/null and "b/public/ut/2\354\243\274\354\260\250_2.jpg" differ diff --git "a/public/ut/2\354\243\274\354\260\250_3.jpg" "b/public/ut/2\354\243\274\354\260\250_3.jpg" new file mode 100644 index 00000000..01fe3985 Binary files /dev/null and "b/public/ut/2\354\243\274\354\260\250_3.jpg" differ diff --git "a/public/ut/2\354\243\274\354\260\250_4.jpg" "b/public/ut/2\354\243\274\354\260\250_4.jpg" new file mode 100644 index 00000000..a5d681a8 Binary files /dev/null and "b/public/ut/2\354\243\274\354\260\250_4.jpg" differ diff --git a/public/ut/3.jpg b/public/ut/3.jpg new file mode 100644 index 00000000..97a0b32f Binary files /dev/null and b/public/ut/3.jpg differ diff --git "a/public/ut/3\354\243\274\354\260\250_1.jpg" "b/public/ut/3\354\243\274\354\260\250_1.jpg" new file mode 100644 index 00000000..545a289d Binary files /dev/null and "b/public/ut/3\354\243\274\354\260\250_1.jpg" differ diff --git "a/public/ut/3\354\243\274\354\260\250_2.jpg" "b/public/ut/3\354\243\274\354\260\250_2.jpg" new file mode 100644 index 00000000..f1144c0d Binary files /dev/null and "b/public/ut/3\354\243\274\354\260\250_2.jpg" differ diff --git "a/public/ut/3\354\243\274\354\260\250_3.jpg" "b/public/ut/3\354\243\274\354\260\250_3.jpg" new file mode 100644 index 00000000..9427ae76 Binary files /dev/null and "b/public/ut/3\354\243\274\354\260\250_3.jpg" differ diff --git a/public/ut/4.jpg b/public/ut/4.jpg new file mode 100644 index 00000000..a2ee9440 Binary files /dev/null and b/public/ut/4.jpg differ diff --git a/public/ut/5.jpg b/public/ut/5.jpg new file mode 100644 index 00000000..140df0d9 Binary files /dev/null and b/public/ut/5.jpg differ diff --git a/public/ut/6.jpg b/public/ut/6.jpg new file mode 100644 index 00000000..575a2813 Binary files /dev/null and b/public/ut/6.jpg differ diff --git a/public/ut/7.jpg b/public/ut/7.jpg new file mode 100644 index 00000000..ecce28c2 Binary files /dev/null and b/public/ut/7.jpg differ diff --git a/public/ut/8.jpg b/public/ut/8.jpg new file mode 100644 index 00000000..ba8d133c Binary files /dev/null and b/public/ut/8.jpg differ diff --git a/public/ut/9.jpg b/public/ut/9.jpg new file mode 100644 index 00000000..491e5a65 Binary files /dev/null and b/public/ut/9.jpg differ diff --git a/public/vercel.svg b/public/vercel.svg new file mode 100644 index 00000000..77053960 --- /dev/null +++ b/public/vercel.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/public/window.svg b/public/window.svg new file mode 100644 index 00000000..b2b2a44f --- /dev/null +++ b/public/window.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/scripts/generate-color-token.mjs b/scripts/generate-color-token.mjs new file mode 100644 index 00000000..04d57b3e --- /dev/null +++ b/scripts/generate-color-token.mjs @@ -0,0 +1,626 @@ +import { readFile, writeFile } from 'node:fs/promises'; +import { resolve } from 'node:path'; + +function toKebabCase(input) { + return String(input) + .replaceAll(/[^a-zA-Z0-9]+/g, '-') + .replaceAll(/([a-z0-9])([A-Z])/g, '$1-$2') + .toLowerCase() + .replaceAll(/-{2,}/g, '-') + .replaceAll(/^-|-$/g, ''); +} + +function isColorToken(node) { + return ( + node && + typeof node === 'object' && + 'value' in node && + 'type' in node && + node.type === 'color' + ); +} + +function isTypedToken(node) { + return node && typeof node === 'object' && 'value' in node && 'type' in node; +} + +function isPlainObject(value) { + return value && typeof value === 'object' && !Array.isArray(value); +} + +function flattenColorTokens( + root, + pathParts = [], + out = [], + pathToVarMap = new Map(), + refPrefixSegments = [], + refParts = [], +) { + if (!root || typeof root !== 'object') return { tokens: out, pathToVarMap }; + + if (isColorToken(root)) { + const variableName = `--${toKebabCase(pathParts.join('-'))}`; + out.push({ name: variableName, value: root.value }); + // Save mapping from dotted path (e.g. Color.Blue.500) to variable name (e.g. --color-blue-500) + const dottedKey = pathParts.join('.'); + pathToVarMap.set(dottedKey, variableName); + return { tokens: out, pathToVarMap }; + } + + for (const [key, value] of Object.entries(root)) { + const nextPath = [...pathParts, toKebabCase(key)]; + // Track JSON reference path using original keys only (no kebab-case) + const nextRefParts = [...refParts, key]; + if (isColorToken(value)) { + const varName = `--${toKebabCase(nextPath.join('-'))}`; + out.push({ name: varName, value: value.value }); + const refKey = [...refPrefixSegments, ...nextRefParts].join('.'); + const canonicalRefKey = refKey + .split('.') + .map((part) => part.replaceAll(' ', '')) + .join('.'); + pathToVarMap.set(refKey, varName); + pathToVarMap.set(canonicalRefKey, varName); + } else if (value && typeof value === 'object') { + flattenColorTokens( + value, + nextPath, + out, + pathToVarMap, + refPrefixSegments, + nextRefParts, + ); + } + } + return { tokens: out, pathToVarMap }; +} + +function flattenSemanticTokens(root, pathParts = [], out = []) { + if (!root || typeof root !== 'object') return out; + if (isTypedToken(root)) { + out.push({ path: pathParts, value: root.value, type: root.type }); + return out; + } + for (const [key, value] of Object.entries(root)) { + flattenSemanticTokens(value, [...pathParts, toKebabCase(key)], out); + } + return out; +} + +function resolveReferenceValue(value, pathToVarMap) { + if (typeof value !== 'string') return null; + const match = value.match(/^\{([^}]+)\}$/); + if (!match) return null; + let ref = match[1]; // e.g., Color.Blue.500 + // Try direct key first + if (pathToVarMap.has(ref)) return `var(${pathToVarMap.get(ref)})`; + // Try normalized key (remove spaces and normalize case of first segment) + const parts = ref.split('.'); + if (parts.length > 0) parts[0] = parts[0].trim(); + const canonical = parts.map((p) => p.replaceAll(' ', '')).join('.'); + if (pathToVarMap.has(canonical)) return `var(${pathToVarMap.get(canonical)})`; + // Last attempt: lower-cased, kebab combined map + const lower = parts.map((p) => p.toLowerCase()).join('.'); + if (pathToVarMap.has(lower)) return `var(${pathToVarMap.get(lower)})`; + return null; +} + +// Map common weight names to numeric values + +function toPx(value) { + if (typeof value === 'number') return `${value}px`; + if (typeof value === 'string') + return /px$/.test(value) ? value : `${value}px`; + return String(value); +} + +function collectTextPrimitives(primitiveRoot) { + const textRoot = {}; + if (!isPlainObject(primitiveRoot)) return textRoot; + + const fontRoot = primitiveRoot.font; + if (!isPlainObject(fontRoot)) return textRoot; + + // Extract font family + const fontFamily = fontRoot.family?.pretendard?.value; + if (fontFamily) { + textRoot.font = { fontFamily }; + } + + // Extract font weights + const fontWeights = {}; + if (isPlainObject(fontRoot.weight)) { + for (const [wKey, wVal] of Object.entries(fontRoot.weight)) { + if (isTypedToken(wVal)) { + fontWeights[wKey] = wVal.value; + } + } + } + + // Extract sizes and line heights + const sizes = fontRoot.size || {}; + const lineHeights = fontRoot['line height'] || {}; + const letterSpacings = fontRoot['letter spacing'] || {}; + + // Create font tokens for each size + for (const [sizeKey, sizeObj] of Object.entries(sizes)) { + if (!isTypedToken(sizeObj)) continue; + + const sizeValue = sizeObj.value; + const lineHeightObj = lineHeights[sizeKey]; + const letterSpacingObj = letterSpacings[sizeKey]; + + const sizeName = `size-${sizeKey}`; + textRoot[sizeName] = { + fontSize: toPx(sizeValue), + }; + + if (isTypedToken(lineHeightObj)) { + textRoot[sizeName].lineHeight = toPx(lineHeightObj.value); + } + + if (isTypedToken(letterSpacingObj)) { + textRoot[sizeName].letterSpacing = toPx(letterSpacingObj.value); + } + } + + // Add font weights to textRoot + if (Object.keys(fontWeights).length > 0) { + textRoot.weight = { fontWeight: fontWeights }; + } + + return textRoot; +} + +function buildTextRootCss(textRoot) { + const lines = []; + for (const [groupName, groupVal] of Object.entries(textRoot)) { + if (groupName === 'font' && groupVal.fontFamily) { + lines.push(` --font-family-primary: ${groupVal.fontFamily};`); + } + if (groupName === 'weight' && groupVal.fontWeight) { + for (const [wKey, wVal] of Object.entries(groupVal.fontWeight)) { + lines.push(` --font-weight-${wKey}: ${wVal};`); + } + } + if (groupName.startsWith('size-')) { + const sizeKey = groupName.replace('size-', ''); + if (groupVal.fontSize) { + lines.push(` --font-size-${sizeKey}: ${groupVal.fontSize};`); + } + if (groupVal.lineHeight) { + lines.push(` --line-height-${sizeKey}: ${groupVal.lineHeight};`); + } + if (groupVal.letterSpacing) { + lines.push(` --letter-spacing-${sizeKey}: ${groupVal.letterSpacing};`); + } + } + } + return lines; +} + +function buildTextThemeCss(textRoot) { + const lines = []; + for (const [groupName, groupVal] of Object.entries(textRoot)) { + if (groupName === 'font' && groupVal.fontFamily) { + lines.push(` --font-primary: var(--font-family-primary);`); + } + if (groupName.startsWith('size-')) { + const sizeKey = groupName.replace('size-', ''); + if (groupVal.fontSize) { + lines.push(` --text-${sizeKey}: var(--font-size-${sizeKey});`); + } + if (groupVal.lineHeight) { + lines.push(` --leading-${sizeKey}: var(--line-height-${sizeKey});`); + } + if (groupVal.letterSpacing) { + lines.push( + ` --tracking-${sizeKey}: var(--letter-spacing-${sizeKey});`, + ); + } + } + } + return lines; +} + +// 새로운 typography utility 생성: token.json의 title/body/heading/caption 등 스타일 네이밍 기반 +function buildTypographyUtilitiesCssFromToken(token) { + const lines = []; + const start = '/* generated-typography-utilities:start */'; + const end = '/* generated-typography-utilities:end */'; + lines.push(start); + + // 지원하는 스타일 그룹 (global에서) + const styleGroups = ['title', 'heading', 'body', 'caption']; + + // fontSize/weight 매핑용 + const sizeMap = { + 0: '12', + 1: '13', + 2: '14', + 3: '15', + 4: '16', + 5: '17', + 6: '18', + 7: '20', + 8: '24', + 9: '28', + 10: '32', + 11: '36', + 12: '40', + 13: '44', + }; + + const weightMap = { + 'pretendard-0': '700', + 'pretendard-1': '600', + 'pretendard-2': '500', + 'pretendard-3': '400', + }; + + // 그룹별 유틸리티 생성 + for (const group of styleGroups) { + const groupObj = token[group]; + + if (!groupObj) continue; + + for (const sizeKey of Object.keys(groupObj)) { + const sizeObj = groupObj[sizeKey]; + for (const weightKey of Object.keys(sizeObj)) { + const styleObj = sizeObj[weightKey]; + if (!styleObj || styleObj.type !== 'typography') continue; + + const value = styleObj.value; + + // 클래스명: typo-{group}-{sizeKey}-{weightKey} + // ex) @utility typo-body-lg-semibold { … } + const utilityName = `@utility typo-${group}-${sizeKey}-${weightKey} {`; + lines.push(utilityName); + + // font-family + if (value.fontFamily) { + lines.push(` font-family: var(--font-primary);`); + } + + // font-weight + let weightValue = '400'; + if (value.fontWeight) { + const weightRef = value.fontWeight; + if (weightRef.includes('pretendard-0')) weightValue = '700'; + else if (weightRef.includes('pretendard-1')) weightValue = '600'; + else if (weightRef.includes('pretendard-2')) weightValue = '500'; + else if (weightRef.includes('pretendard-3')) weightValue = '400'; + lines.push(` font-weight: ${weightValue};`); + } + + // font-size + let actualSize = '16'; + if (value.fontSize) { + const sizeRef = value.fontSize; + const sizeMatch = sizeRef.match(/fontSize\.(\d+)/); + if (sizeMatch) { + actualSize = sizeMap[sizeMatch[1]] || '16'; + lines.push(` font-size: var(--font-size-${actualSize});`); + } + } + + // letter-spacing + if (value.letterSpacing && !value.letterSpacing.includes('0')) { + lines.push(` letter-spacing: var(--letter-spacing-0);`); + } + + // line-height + if (value.lineHeight) { + const lineRef = value.lineHeight.match(/(\d+)/); + if (lineRef) { + lines.push(` line-height: var(--line-height-${lineRef[1]});`); + } + } + + lines.push('}'); + lines.push(''); // 줄바꿈 + } + } + } + + // dropshadow 스타일 처리 + for (const key of Object.keys(token)) { + const shadowObj = token[key]; + + if ( + shadowObj.type === 'boxShadow' && + shadowObj.value.type === 'dropShadow' + ) { + const value = shadowObj.value; + const shadowKey = key; // dropshadow-{blur}-{spread} + + // 클래스명: drop-shadow-{blur}-{spread} + const shadowUtility = `@utility drop-shadow-${value.blur}-${value.spread} {`; + lines.push(shadowUtility); + + // box-shadow 속성 추가 + const boxShadowValue = `${value.x}px ${value.y}px ${value.blur}px ${value.spread}px ${value.color}`; + lines.push(` box-shadow: ${boxShadowValue};`); + lines.push('}'); + lines.push(''); + } + } + + lines.push(end); + return lines; +} + +async function main() { + const projectRoot = process.cwd(); + const inputPath = resolve(projectRoot, 'src/app/token.json'); + const globalsPath = resolve(projectRoot, 'src/app/globals.css'); + + const raw = await readFile(inputPath, 'utf8'); + const tokens = JSON.parse(raw); + + const primitiveMode = tokens['primitive/Mode 1'] || {}; + const primitive = primitiveMode?.color; + if (!primitive) { + console.error( + 'No primitive color tokens found at "primitive/Mode 1" → "color"', + ); + } + + const { tokens: colorVars, pathToVarMap } = flattenColorTokens( + primitive, + ['color'], + [], + new Map(), + ['color'], + [], + ); + + // Use semantic/Mode 1 as the main semantic tokens + const semanticRoot = tokens['semantic/Mode 1']; + const semanticTokens = semanticRoot + ? flattenSemanticTokens(semanticRoot) + : []; + + // Collect text primitives from primitive/Mode 1 (excluding Color) + const textPrimitives = collectTextPrimitives(primitiveMode); + + // typography utility를 token.json의 스타일 네이밍 기반으로 생성 + const typographyUtilityLines = buildTypographyUtilitiesCssFromToken( + tokens.global || {}, + ); + + // Read globals.css to inject tokens + let globalsCss = await readFile(globalsPath, 'utf8'); + + // Remove any tokens.css import if exists + globalsCss = globalsCss.replace( + /\n?@import\s+["']\.\.\/styles\/tokens\.css["'];?\n?/g, + '\n', + ); + + // Helper to find block range by selector start index + function findBlockRange(source, startIndexOfBrace) { + let depth = 0; + for (let i = startIndexOfBrace; i < source.length; i++) { + const ch = source[i]; + if (ch === '{') depth++; + else if (ch === '}') { + depth--; + if (depth === 0) { + return { start: startIndexOfBrace, end: i }; + } + } + } + return null; + } + + // Locate top-level :root block (first occurrence) + const rootSelRegex = /:root\s*\{/g; + const rootSelMatch = rootSelRegex.exec(globalsCss); + if (!rootSelMatch) { + throw new Error('Could not find :root block in src/app/globals.css'); + } + const rootBlock = findBlockRange( + globalsCss, + rootSelMatch.index + rootSelMatch[0].length - 1, + ); + if (!rootBlock) throw new Error('Failed to parse :root block'); + + // Prepare primitive color tokens content + const primitiveStart = '/* generated-color-tokens:start */'; + const primitiveEnd = '/* generated-color-tokens:end */'; + const primitiveLines = [primitiveStart]; + for (const { name, value } of colorVars) { + primitiveLines.push(` ${name}: ${value};`); + } + primitiveLines.push(primitiveEnd); + const primitiveBlock = '\n' + primitiveLines.join('\n') + '\n'; + + // Prepare primitive text tokens content + const textStart = '/* generated-text-tokens:start */'; + const textEnd = '/* generated-text-tokens:end */'; + const textLines = [textStart, ...buildTextRootCss(textPrimitives), textEnd]; + const textBlock = '\n' + textLines.join('\n') + '\n'; + + // Replace or insert into :root block + const rootContent = globalsCss.slice(rootBlock.start + 1, rootBlock.end); + const existingPrimitiveRe = new RegExp( + primitiveStart.replace(/[/*]/g, (m) => `\\${m}`) + + '[\\s\\S]*?' + + primitiveEnd.replace(/[/*]/g, (m) => `\\${m}`), + 'm', + ); + let newRootContent; + if (existingPrimitiveRe.test(rootContent)) { + newRootContent = rootContent.replace( + existingPrimitiveRe, + primitiveBlock.trim(), + ); + } else { + newRootContent = rootContent.replace(/\n\s*\}$/, '') + primitiveBlock + '}'; + // The '}' will be re-added when we reconstruct full content below; ensure no duplicate + newRootContent = newRootContent.replace(/\}\s*$/, ''); + } + + // Reconstruct globals with updated :root + globalsCss = + globalsCss.slice(0, rootBlock.start + 1) + + newRootContent + + globalsCss.slice(rootBlock.end); + + // Inject or replace text tokens in :root + const rootSelMatch2 = /:root\s*\{/g.exec(globalsCss); + if (!rootSelMatch2) throw new Error(':root disappeared unexpectedly'); + const rootBlock2 = findBlockRange( + globalsCss, + rootSelMatch2.index + rootSelMatch2[0].length - 1, + ); + const rootContent2 = globalsCss.slice(rootBlock2.start + 1, rootBlock2.end); + const existingTextRe = new RegExp( + textStart.replace(/[/*]/g, (m) => `\\${m}`) + + '[\\s\\S]*?' + + textEnd.replace(/[/*]/g, (m) => `\\${m}`), + 'm', + ); + let newRootContent2; + if (existingTextRe.test(rootContent2)) { + newRootContent2 = rootContent2.replace(existingTextRe, textBlock.trim()); + } else { + newRootContent2 = rootContent2.replace(/\n\s*\}$/, '') + textBlock + '}'; + newRootContent2 = newRootContent2.replace(/\}\s*$/, ''); + } + globalsCss = + globalsCss.slice(0, rootBlock2.start + 1) + + newRootContent2 + + globalsCss.slice(rootBlock2.end); + + // Locate @theme inline block + const themeRegex = /@theme\s+inline\s*\{/g; + const themeMatch = themeRegex.exec(globalsCss); + if (!themeMatch) { + throw new Error( + 'Could not find "@theme inline {" block in src/app/globals.css', + ); + } + const themeBlock = findBlockRange( + globalsCss, + themeMatch.index + themeMatch[0].length - 1, + ); + if (!themeBlock) throw new Error('Failed to parse @theme inline block'); + + // Prepare semantic tokens (without 'semantic' prefix) to go into @theme inline + const semanticStart = '/* generated-semantic-tokens:start */'; + const semanticEnd = '/* generated-semantic-tokens:end */'; + const semanticLines = [semanticStart]; + for (const { path, value, type } of semanticTokens) { + if (type !== 'color') continue; + const varName = `--${toKebabCase(path.join('-'))}`; // e.g., --color-bg-interactive-primary + const refValue = resolveReferenceValue(value, pathToVarMap); + const finalValue = refValue ?? value; + semanticLines.push(` ${varName}: ${finalValue};`); + } + semanticLines.push(semanticEnd); + const semanticBlock = '\n' + semanticLines.join('\n') + '\n'; + + // Prepare typography theme tokens + const typoStart = '/* generated-typography-tokens:start */'; + const typoEnd = '/* generated-typography-tokens:end */'; + const typoLines = [typoStart, ...buildTextThemeCss(textPrimitives), typoEnd]; + const typoBlock = '\n' + typoLines.join('\n') + '\n'; + + const themeContent = globalsCss.slice(themeBlock.start + 1, themeBlock.end); + const existingSemanticRe = new RegExp( + semanticStart.replace(/[/*]/g, (m) => `\\${m}`) + + '[\\s\\S]*?' + + semanticEnd.replace(/[/*]/g, (m) => `\\${m}`), + 'm', + ); + let newThemeContent; + if (existingSemanticRe.test(themeContent)) { + newThemeContent = themeContent.replace( + existingSemanticRe, + semanticBlock.trim(), + ); + } else { + newThemeContent = + themeContent.replace(/\n\s*\}$/, '') + semanticBlock + '}'; + newThemeContent = newThemeContent.replace(/\}\s*$/, ''); + } + + globalsCss = + globalsCss.slice(0, themeBlock.start + 1) + + newThemeContent + + globalsCss.slice(themeBlock.end); + + // Inject or replace typography theme tokens + const themeMatch2 = /@theme\s+inline\s*\{/g.exec(globalsCss); + if (!themeMatch2) throw new Error('@theme inline disappeared unexpectedly'); + const themeBlock2 = findBlockRange( + globalsCss, + themeMatch2.index + themeMatch2[0].length - 1, + ); + const themeContent2 = globalsCss.slice( + themeBlock2.start + 1, + themeBlock2.end, + ); + const existingTypoRe = new RegExp( + typoStart.replace(/[/*]/g, (m) => `\\${m}`) + + '[\\s\\S]*?' + + typoEnd.replace(/[/*]/g, (m) => `\\${m}`), + 'm', + ); + let newThemeContent2; + if (existingTypoRe.test(themeContent2)) { + newThemeContent2 = themeContent2.replace(existingTypoRe, typoBlock.trim()); + } else { + newThemeContent2 = themeContent2.replace(/\n\s*\}$/, '') + typoBlock + '}'; + newThemeContent2 = newThemeContent2.replace(/\}\s*$/, ''); + } + + globalsCss = + globalsCss.slice(0, themeBlock2.start + 1) + + newThemeContent2 + + globalsCss.slice(themeBlock2.end); + + // Inject or replace typography utilities at file root + const utilStart = '/* generated-typography-utilities:start */'; + const utilEnd = '/* generated-typography-utilities:end */'; + const utilBlock = typographyUtilityLines.join('\n') + '\n'; + const utilRe = new RegExp( + utilStart.replace(/[/*]/g, (m) => `\\${m}`) + + '[\\s\\S]*?' + + utilEnd.replace(/[/*]/g, (m) => `\\${m}`), + 'm', + ); + if (utilRe.test(globalsCss)) { + globalsCss = globalsCss.replace(utilRe, utilBlock.trim()); + } else { + // Append at end of file with a preceding newline + globalsCss = globalsCss.replace(/\s*$/, '\n' + utilBlock); + } + + // body에 font-family 자동 생성/치환 + const bodyFontFamily = 'font-family: var(--font-pretendard);'; + const bodyRe = /body\s*\{[^}]*\}/m; + if (bodyRe.test(globalsCss)) { + globalsCss = globalsCss.replace(bodyRe, (match) => { + // 기존 font-family 라인 치환 또는 추가 + if (/font-family:[^;]+;/.test(match)) { + return match.replace(/font-family:[^;]+;/, bodyFontFamily); + } + // font-family가 없으면 추가 + return match.replace(/\}/, ` ${bodyFontFamily}\n}`); + }); + } else { + // body 블록이 없으면 새로 추가 + globalsCss += `\nbody {\n ${bodyFontFamily}\n}`; + } + + await writeFile(globalsPath, globalsCss, 'utf8'); + console.log(`Updated ${globalsPath}`); +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/scripts/generate-endpoint.ts b/scripts/generate-endpoint.ts new file mode 100644 index 00000000..e40297b1 --- /dev/null +++ b/scripts/generate-endpoint.ts @@ -0,0 +1,482 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +// scripts/generate-ep.ts +// Usage: +// npx ts-node scripts/generate-ep.ts +// npx ts-node scripts/generate-ep.ts https://dev.say-cheese.me/v3/api-docs +// +// Output: +// src/api/ep.ts (EP only + PhotoSorting type) + +import fs from 'node:fs'; +import path from 'node:path'; +import process from 'node:process'; + +const SPEC_URL = process.argv[2] || 'https://dev.say-cheese.me/v3/api-docs'; +const OUT_FILE = path.join(process.cwd(), 'src/global/api/ep.ts'); + +type Route = { path: string; method: string }; + +// --- 그룹 매핑 (경로 prefix 기반 강제 매핑) --- +function groupKeyByPath(p: string): keyof typeof EP_SKELETON { + if (p.startsWith('/v1/global/')) return 'global'; + if (p.startsWith('/v1/auth/')) return 'auth'; + if (p.startsWith('/v1/user/')) return 'user'; + if (p.startsWith('/v1/cheese4cut/')) return 'cheese4cut'; + if (p.startsWith('/v1/album/')) return 'album'; + if (p.startsWith('/v1/photo/')) return 'photo'; + if (p.startsWith('/internal/')) return 'internal'; + // 기본값: album로 (실제 스펙 상 여기에 안걸림) + return 'album'; +} + +function desiredNameFor(pathStr: string, method: string): string { + // 1) 기존 정확 매핑 우선 + // ---------------------------------- + // Global + if (pathStr === '/v1/global/health-check') return 'health'; + + // Auth + if (pathStr === '/v1/auth/logout') return 'logout'; + if (pathStr === '/v1/auth/reissue') return 'reissue'; + if (pathStr === '/v1/auth/exchange') return 'exchange'; + + // User + if (pathStr === '/v1/user/agreement') return 'agreement'; + if (pathStr === '/v1/user/me/profile') return 'updateProfile'; + + // Album + if (pathStr === '/v1/album' && method === 'post') return 'create'; + if (/^\/v1\/album\/\{code\}\/enter$/.test(pathStr)) return 'enter'; + if (/^\/v1\/album\/\{code\}\/photos$/.test(pathStr)) return 'photos'; + if (/^\/v1\/album\/\{code\}\/photos\/\{photoId\}$/.test(pathStr)) + return 'photoDetail'; + if (/^\/v1\/album\/\{code\}\/photos\/liked$/.test(pathStr)) + return 'likedPhotos'; + if (/^\/v1\/album\/\{code\}\/participants$/.test(pathStr)) + return 'participants'; + if (/^\/v1\/album\/\{code\}\/invitation$/.test(pathStr)) return 'invitation'; + if (/^\/v1\/album\/\{code\}\/available-count$/.test(pathStr)) + return 'availableCount'; + + // Photo + if (/^\/v1\/photo\/\{photoId\}\/liked$/.test(pathStr)) return 'like'; + if (/^\/v1\/photo\/\{photoId\}\/unliked$/.test(pathStr)) return 'unlike'; + if (pathStr === '/v1/photo/report') return 'reportUploadResult'; + if (pathStr === '/v1/photo/presigned-url') return 'presignedUpload'; + if (pathStr === '/v1/photo/download-url') return 'presignedDownload'; + + // Cheese4cut + if (/^\/v1\/cheese4cut\/\{code\}\/presigned-url$/.test(pathStr)) + return 'presignedUpload'; + if (/^\/v1\/cheese4cut\/\{code\}\/fixed$/.test(pathStr)) return 'finalize'; + if (/^\/v1\/cheese4cut\/\{code\}\/preview$/.test(pathStr)) return 'preview'; + + // Internal + if (pathStr === '/internal/thumbnail/complete') return 'thumbnailComplete'; + + // 2) 여기부터 자동 fallback 생성 + // ---------------------------------- + return autoName(pathStr); +} + +function autoName(pathStr: string): string { + const parts = pathStr + .split('/') + .filter(Boolean) + .filter((p) => !p.startsWith('{')) // 경로 파라미터 제거 + .filter((p) => !/^v\d+$/.test(p)); // v1, v2 같은 prefix 제거 + + // 하이픈(-)을 카멜케이스로 변환 + const toCamel = (str: string) => + str.replace(/-([a-zA-Z])/g, (_, c) => c.toUpperCase()); + + const camel = parts + .map((p, i) => { + const camelPart = toCamel(p); + return i === 0 + ? camelPart.toLowerCase() + : camelPart.charAt(0).toUpperCase() + camelPart.slice(1); + }) + .join(''); + + return camel || 'unknown'; +} + +function paramsSignatureFor(pathStr: string): string { + const hasCode = pathStr.includes('{code}'); + const hasPhotoId = pathStr.includes('{photoId}'); + + // 1) code + photoId 둘 다 있을 때 + if (hasCode && hasPhotoId) { + return '(code: string, photoId: number)'; + } + + // 2) code만 있을 때 + if (hasCode) { + return '(code: string)'; + } + + // 3) photoId만 있을 때 + if (hasPhotoId) { + return '(photoId: number)'; + } + + return '()'; +} + +// --- 바디: 템플릿 문자열 경로 --- +function pathTemplateToTs(pathStr: string): string { + // {param} -> ${param} + return '`' + pathStr.replace(/{([^}]+)}/g, '${$1}') + '`'; +} + +// EP 골격 (키 순서 고정) +const EP_SKELETON = { + global: [] as Route[], + auth: [] as Route[], + user: [] as Route[], + album: [] as Route[], + photo: [] as Route[], + cheese4cut: [] as Route[], + internal: [] as Route[], +}; + +function sortByDesiredOrder(routes: Route[]) { + // 그냥 path 기준 정렬(안정적) + return routes.slice().sort((a, b) => a.path.localeCompare(b.path)); +} + +function renderGroup(name: keyof typeof EP_SKELETON, routes: Route[]): string { + const lines: string[] = []; + + sortByDesiredOrder(routes).forEach(({ path: p, method }) => { + const fn = desiredNameFor(p, method); + if (!fn) return; + + const sig = paramsSignatureFor(p); + const pathExpr = pathTemplateToTs(p); + + // 🔥 key 항상 문자열 처리 + lines.push(` "${fn}": ${sig} => ${pathExpr},`); + }); + + return ` ${name}: {\n${lines.join('\n')}\n }`; +} + +// ...위쪽 동일 + +type Schema = + | { + type?: string; + enum?: any[]; + items?: Schema; + properties?: Record; + required?: string[]; + allOf?: Schema[]; + oneOf?: Schema[]; + anyOf?: Schema[]; + $ref?: string; + additionalProperties?: boolean | Schema; + description?: string; + } + | any; + +// [ADD] OpenAPI 확장 +type OpenAPI = { + paths: Record< + string, + Record< + string, + { + operationId?: string; + tags?: string[]; + summary?: string; + description?: string; + parameters?: Array<{ + name: string; + in: 'path' | 'query' | 'header' | 'cookie'; + required?: boolean; + schema?: Schema; + }>; + requestBody?: { + content?: Record; + }; + responses?: Record< + string, + { + description?: string; + content?: Record; + } + >; + } + > + >; + components?: { + schemas?: Record; + }; +}; + +// [ADD] 파스칼 케이스 유틸(안전하게 이름 정리) +function toPascalCase(s: string) { + return s + .replace(/[^a-zA-Z0-9]+/g, ' ') + .trim() + .split(' ') + .map((w) => w.charAt(0).toUpperCase() + w.slice(1)) + .join(''); +} + +// [ADD] Components 타입 이름 규칙 (Schema 접미사) +function componentTypeName(raw: string) { + return `${toPascalCase(raw)}Schema`; +} + +// [CHANGE] $ref 해석 시 components 타입명 치환 +function resolveRef(ref: string) { + // '#/components/schemas/Foo' 형태만 지원 + const parts = ref.split('/'); + const raw = parts[parts.length - 1]; + return componentTypeName(raw); // <-- 여기! +} + +// [CHANGE] components.schemas -> 타입 생성 시 이름 변경 +function generateComponentsTypes(components: Record): string { + const lines: string[] = []; + Object.entries(components).forEach(([name, schema]) => { + const ts = schemaToTs(schema, components, name); + const outName = componentTypeName(name); // <-- 여기! + + const isObjectLike = /^\{\s/.test(ts); + if (isObjectLike) { + lines.push(`export interface ${outName} ${ts}`); + } else { + lines.push(`export type ${outName} = ${ts};`); + } + }); + return lines.join('\n'); +} +// [ADD] 스키마 -> TS 변환기 (기본형/enum/array/object/$ref) +function schemaToTs( + schema: Schema | undefined, + components: Record, + inlineNameHint?: string, +): string { + if (!schema) return 'unknown'; + + // $ref + if (schema.$ref) { + return resolveRef(schema.$ref); + } + + // allOf (간단히 & 교차) + if (schema.allOf && schema.allOf.length > 0) { + return schema.allOf.map((s: any) => schemaToTs(s, components)).join(' & '); + } + + // oneOf/anyOf (간단히 union) + if ( + (schema.oneOf && schema.oneOf.length) || + (schema.anyOf && schema.anyOf.length) + ) { + const arr = (schema.oneOf ?? schema.anyOf)!; + return arr.map((s: any) => schemaToTs(s, components)).join(' | '); + } + + // enum + if (schema.enum && schema.enum.length) { + return schema.enum + .map((v: any) => (typeof v === 'string' ? JSON.stringify(v) : `${v}`)) + .join(' | '); + } + + const t = schema.type; + + if ( + t === 'string' || + t === 'number' || + t === 'integer' || + t === 'boolean' || + t === 'null' + ) { + if (t === 'integer') return 'number'; + return t === 'null' ? 'null' : t; + } + + if (t === 'array') { + const it = schema.items ? schemaToTs(schema.items, components) : 'unknown'; + return `${it}[]`; + } + + if (t === 'object' || schema.properties || schema.additionalProperties) { + const props = schema.properties ?? {}; + const req = new Set(schema.required ?? []); + const body: string[] = []; + + for (const [k, v] of Object.entries(props)) { + const optional = req.has(k) ? '' : '?'; + body.push( + `${JSON.stringify(k)}${optional}: ${schemaToTs(v, components, k)};`, + ); + } + + // index signature + if (schema.additionalProperties) { + const ap = + schema.additionalProperties === true + ? 'unknown' + : schemaToTs(schema.additionalProperties, components); + body.push(`[key: string]: ${ap};`); + } + + return `{ ${body.join(' ')} }`; + } + + // fallback + return 'unknown'; +} + +// [ADD] 응답 스키마 추출(200/201/2xx 우선) +function pickSuccessResponseSchema(op: any): Schema | undefined { + const res = op.responses || {}; + const codes = Object.keys(res).sort(); // 안정 + const prefer = [ + '200', + '201', + ...codes.filter((c) => /^2\d\d$/.test(c) && c !== '200' && c !== '201'), + ]; + for (const code of prefer) { + const content = res[code]?.content; + if (!content) continue; + // JSON 우선 + const jsonKey = + Object.keys(content).find((k) => k.includes('json')) || + Object.keys(content)[0]; + const sch = content[jsonKey]?.schema; + if (sch) return sch; + } + return undefined; +} + +// [ADD] 이름 규칙: 그룹+함수명 기반 Response 타입명 +function responseTypeName(group: string, fn: string) { + const cap = (s: string) => toPascalCase(s); + return `${cap(group)}${cap(fn)}Response`; +} + +// [ADD] 2패스: 타입 별칭/인터페이스 모아 쓰고, ApiReturns는 마지막에 +function buildAllOperationTypesAndApiReturns( + groups: typeof EP_SKELETON, + spec: OpenAPI, +) { + const components = spec.components?.schemas ?? {}; + const typeDecls: string[] = []; + const apiReturns: string[] = ['export interface ApiReturns {']; + + (Object.keys(EP_SKELETON) as Array).forEach( + (group) => { + const routes = sortByDesiredOrder(groups[group]); + routes.forEach(({ path: p, method }) => { + const fn = desiredNameFor(p, method); + if (!fn) return; + const op = spec.paths[p]?.[method]; + if (!op) return; + + const respSchema = pickSuccessResponseSchema(op); + if (!respSchema) return; + + const typeName = responseTypeName(group, fn); + const ts = schemaToTs(respSchema, components, typeName); + const isObjectLike = /^\{\s/.test(ts); + + if (isObjectLike) { + typeDecls.push(`export interface ${typeName} ${ts}["result"];`); + } else { + typeDecls.push(`export type ${typeName} = ${ts}["result"];`); + } + + apiReturns.push( + ` ${JSON.stringify(`${group}.${fn}`)}: ${typeName}; // ${method.toUpperCase()} ${p}`, + ); + }); + }, + ); + + apiReturns.push('}'); + return { typeDecls: typeDecls.join('\n'), apiReturns: apiReturns.join('\n') }; +} + +// --- main +async function main() { + console.log(`[generate-ep] Fetching spec: ${SPEC_URL}`); + const res = await fetch(SPEC_URL); + if (!res.ok) { + console.error( + `[generate-ep] Failed to fetch spec: ${res.status} ${res.statusText}`, + ); + process.exit(1); + } + + const spec = (await res.json()) as OpenAPI; + const groups = JSON.parse(JSON.stringify(EP_SKELETON)) as typeof EP_SKELETON; + + Object.entries(spec.paths).forEach(([p, methods]) => { + Object.keys(methods).forEach((m) => { + const method = m.toLowerCase(); + if ( + !['get', 'post', 'put', 'patch', 'delete', 'options', 'head'].includes( + method, + ) + ) + return; + const g = groupKeyByPath(p); + groups[g].push({ path: p, method }); + }); + }); + + // [ADD] components.shema 타입 생성 + const componentsTypes = generateComponentsTypes( + spec.components?.schemas ?? {}, + ); + + // [ADD] 각 오퍼레이션 응답 타입 + ApiReturns 인터페이스 + const { typeDecls, apiReturns } = buildAllOperationTypesAndApiReturns( + groups, + spec, + ); + + const content = `/* AUTO-GENERATED FILE. DO NOT EDIT. + * Generated by scripts/generate-ep.ts + */ + +export const EP = { +${(Object.keys(EP_SKELETON) as Array) + .map((g) => renderGroup(g, groups[g])) + .join(',\n')} +} as const; + +// 선택지 Enum(정렬) +export type PhotoSorting = 'POPULAR' | 'CAPTURED_AT' | 'CREATED_AT'; + +/* ======================= + * Generated Types + * ======================= */ + +// --- Components +${componentsTypes} + +// --- Operation Response Types +${typeDecls} + +// --- Mapping: 'group.fn' -> Response Type +${apiReturns} +`; + + fs.mkdirSync(path.dirname(OUT_FILE), { recursive: true }); + fs.writeFileSync(OUT_FILE, content, 'utf8'); + console.log(`[generate-ep] Wrote ${OUT_FILE}`); +} + +main().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/src/app/album/4cut/[albumId]/page.tsx b/src/app/album/4cut/[albumId]/page.tsx new file mode 100644 index 00000000..5cfd97eb --- /dev/null +++ b/src/app/album/4cut/[albumId]/page.tsx @@ -0,0 +1,53 @@ +import ScreenAlbum4Cut from '@/feature/album/4cut/components/ScreenAlbum4Cut'; +import { ApiReturns, EP } from '@/global/api/ep'; +import { serverApi } from '@/global/utils/serverApi'; +import { Metadata, ResolvingMetadata } from 'next'; + +export async function generateMetadata( + { params }: PageProps, + parent: ResolvingMetadata, +): Promise { + let title = ''; + const { albumId } = await params; + try { + const { result } = await serverApi.get({ + path: EP.album.invitation(albumId), + }); + + if (result?.title) title = result.title; + } catch (e) { + console.error(e); + } + + return { + title: `치즈네컷이 궁금해요 | ${title}`, + description: '메이커님 네컷 확정해주세요 (*´ー`*人)', + openGraph: { + title: `치즈네컷이 궁금해요 | ${title}`, + description: '메이커님 네컷 확정해주세요 (*´ー`*人)', + url: `https://say-cheese.me/album/4cut/${albumId}`, + siteName: '치이이즈', + images: [ + { + url: '/og/toMaker_og.png', + width: 1200, + height: 630, + alt: '치이이즈. 우리 공유앨범에 초대합니다. 일주일 뒤에는 앨범이 사라져요!', + }, + ], + locale: 'ko_KR', + type: 'website', + }, + }; +} + +interface PageProps { + params: Promise<{ + albumId: string; + }>; +} + +export default async function Page({ params }: PageProps) { + const { albumId } = await params; + return ; +} diff --git a/src/app/album/[albumId]/select/page.tsx b/src/app/album/[albumId]/select/page.tsx new file mode 100644 index 00000000..83a6f8bc --- /dev/null +++ b/src/app/album/[albumId]/select/page.tsx @@ -0,0 +1,122 @@ +'use client'; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, +} from '@/components/ui/alert-dialog'; +import SelectAlbumBody from '@/feature/album-select/components/SelectAlbumBody'; +import CustomHeader from '@/global/components/header/CustomHeader'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useRef, useState } from 'react'; + +export default function Page() { + const [modalOpen, setModalOpen] = useState(false); + const confirmedRef = useRef(false); + const router = useRouter(); + const params = useParams(); + const albumId = params?.albumId; + + // data-scroll-locked 속성 제거 + useEffect(() => { + const removeScrollLock = () => { + const body = document.body; + if (body.hasAttribute('data-scroll-locked')) { + body.removeAttribute('data-scroll-locked'); + } + }; + + removeScrollLock(); + const interval = setInterval(removeScrollLock, 100); + + return () => { + clearInterval(interval); + removeScrollLock(); + }; + }, []); + + // 뒤로가기(브라우저/하드웨어) 감지 + useEffect(() => { + const handler = () => { + if (modalOpen) { + setModalOpen(false); // 모달만 닫기 + } else if (!confirmedRef.current) { + setModalOpen(true); + history.pushState(null, '', location.href); // 뒤로가기 무효화 + } + }; + + // 초기 히스토리 상태 추가 + history.pushState(null, '', location.href); + window.addEventListener('popstate', handler); + + return () => { + window.removeEventListener('popstate', handler); + }; + }, [modalOpen]); + + const handleConfirm = () => { + confirmedRef.current = true; + setModalOpen(false); + if (albumId) { + window.location.replace('/main'); + } + }; + + const handleCancel = () => { + // 히스토리 상태 다시 추가만 수행 + history.pushState(null, '', location.href); + }; + + const handleOpenChange = (open: boolean) => { + setModalOpen(open); + if (!open) { + // 모달이 닫힐 때 (바깥 클릭, ESC 등) + handleCancel(); + } + }; + + return ( +
+ setModalOpen(true)} + /> + + + + + + + 앨범 채우기를 그만둘까요? + + + 사진을 앨범에 채우기 전이에요.{'\n'}지금 나가면 다시 사진을 + 불러와야 해요. + + + + + 취소 + + + 나가기 + + + + +
+ ); +} diff --git a/src/app/album/[albumId]/waiting/page.tsx b/src/app/album/[albumId]/waiting/page.tsx new file mode 100644 index 00000000..1db9cf46 --- /dev/null +++ b/src/app/album/[albumId]/waiting/page.tsx @@ -0,0 +1,12 @@ +import WaitingAlbum from '@/feature/create-album/components/WaitingAlbum'; + +type PageProps = { + params: Promise<{ + albumId: string; + }>; +}; + +export default async function Page({ params }: PageProps) { + const { albumId } = await params; + return ; +} diff --git a/src/app/album/detail/[albumId]/page.tsx b/src/app/album/detail/[albumId]/page.tsx new file mode 100644 index 00000000..23c984fd --- /dev/null +++ b/src/app/album/detail/[albumId]/page.tsx @@ -0,0 +1,13 @@ +import ScreenAlbumDetail from '@/feature/album/detail/components/ScreenAlbumDetail'; + +interface PageProps { + params: Promise<{ + albumId: string; + }>; +} + +export default async function Page({ params }: PageProps) { + const { albumId } = await params; + + return ; +} diff --git a/src/app/album/detail/[albumId]/sidebar/page.tsx b/src/app/album/detail/[albumId]/sidebar/page.tsx new file mode 100644 index 00000000..6faab89a --- /dev/null +++ b/src/app/album/detail/[albumId]/sidebar/page.tsx @@ -0,0 +1,12 @@ +import ScreenAlbumSidebar from '@/feature/album/detail/sidebar/components/ScreenAlbumSidebar'; + +interface PageProps { + params: Promise<{ + albumId: string; + }>; +} + +export default async function Page({ params }: PageProps) { + const { albumId } = await params; + return ; +} diff --git a/src/app/album/entry/[albumId]/page.tsx b/src/app/album/entry/[albumId]/page.tsx new file mode 100644 index 00000000..9ce46732 --- /dev/null +++ b/src/app/album/entry/[albumId]/page.tsx @@ -0,0 +1,52 @@ +import ScreenAlbumEntry from '@/feature/album-entry/components/ScreenAlbumEntry'; +import { ApiReturns, EP } from '@/global/api/ep'; +import { serverApi } from '@/global/utils/serverApi'; +import { Metadata, ResolvingMetadata } from 'next'; + +export async function generateMetadata( + { params }: PageProps, + parent: ResolvingMetadata, +): Promise { + let title = ''; + const { albumId } = await params; + try { + const { result } = await serverApi.get({ + path: EP.album.invitation(albumId), + }); + + if (result?.title) title = result.title; + } catch (e) { + console.log(e); + } + + return { + title: `${title} | 앨범에 초대해요`, + description: '치이이즈: 추억은 따끈할 때 제맛', + openGraph: { + title: `${title} | 앨범에 초대해요`, + description: '치이이즈: 추억은 따끈할 때 제맛', + url: `https://say-cheese.me/album/entry/${albumId}`, + siteName: '치이이즈', + images: [ + { + url: '/og/invite_og.png', + width: 1200, + height: 630, + alt: '치이이즈. 우리 공유앨범에 초대합니다. 일주일 뒤에는 앨범이 사라져요!', + }, + ], + locale: 'ko_KR', + type: 'website', + }, + }; +} + +interface PageProps { + params: Promise<{ albumId: string }>; +} + +export default async function Page({ params }: PageProps) { + const { albumId } = await params; + + return ; +} diff --git a/src/app/album/qrcode/[albumId]/page.tsx b/src/app/album/qrcode/[albumId]/page.tsx new file mode 100644 index 00000000..9d1f321a --- /dev/null +++ b/src/app/album/qrcode/[albumId]/page.tsx @@ -0,0 +1,12 @@ +import ScreenAlbumQrcode from '@/feature/album/qrcode/components/ScreenAlbumQrcode'; + +interface PageProps { + params: Promise<{ + albumId: string; + }>; +} + +export default async function Page({ params }: PageProps) { + const { albumId } = await params; + return ; +} diff --git a/src/app/album/upload/[albumId]/page.tsx b/src/app/album/upload/[albumId]/page.tsx new file mode 100644 index 00000000..bbdf11c7 --- /dev/null +++ b/src/app/album/upload/[albumId]/page.tsx @@ -0,0 +1,29 @@ +import UploadAlbumPage from '@/feature/upload/components/UploadAlbumPage'; +import { getAlbumDataWithRoleServer } from '@/feature/upload/hooks/useGetAlbumInform.server'; +import { EP } from '@/global/api/ep'; +import { + dehydrate, + HydrationBoundary, + QueryClient, +} from '@tanstack/react-query'; + +type PageProps = { + params: Promise<{ albumId: string }>; +}; + +export default async function Page({ params }: PageProps) { + const { albumId } = await params; + const queryClient = new QueryClient(); + await queryClient.prefetchQuery({ + queryKey: [EP.album.participants(albumId)], + queryFn: () => getAlbumDataWithRoleServer(albumId), + staleTime: 60 * 1000 * 10, + }); + + const dehydratedState = dehydrate(queryClient); + return ( + + + + ); +} diff --git a/src/app/create-album/[albumId]/complete/page.tsx b/src/app/create-album/[albumId]/complete/page.tsx new file mode 100644 index 00000000..795fe18f --- /dev/null +++ b/src/app/create-album/[albumId]/complete/page.tsx @@ -0,0 +1,10 @@ +import CreateComplete from '@/feature/create-album/components/CreateComplete'; + +export default async function Page({ + params, +}: { + params: Promise<{ albumId: string }>; +}) { + const { albumId } = await params; + return ; +} diff --git a/src/app/create-album/page.tsx b/src/app/create-album/page.tsx new file mode 100644 index 00000000..8022c959 --- /dev/null +++ b/src/app/create-album/page.tsx @@ -0,0 +1,19 @@ +'use client'; +import CreateAlbumList from '@/feature/create-album/components/CreateAlbumList'; +import CustomHeader from '@/global/components/header/CustomHeader'; +import { useRouter } from 'next/navigation'; + +export default function Page() { + const router = useRouter(); + + return ( +
+ router.push('/main')} + /> + +
+ ); +} diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 00000000..d59ae045 --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,715 @@ +@import 'tailwindcss'; +@import "tw-animate-css"; + +@custom-variant dark (&:is(.dark *)); + +:root { + + /* generated-color-tokens:start */ + --color-primary-25: #fffbeb; + --color-primary-50: #fff7d6; + --color-primary-100: #fff2c2; + --color-primary-200: #ffe894; + --color-primary-300: #ffdc5c; + --color-primary-400: #ffcd14; + --color-primary-500: #ffb700; + --color-primary-600: #e09900; + --color-primary-700: #996300; + --color-primary-800: #664200; + --color-primary-900: #332100; + --color-accent-25: #fff5f6; + --color-accent-50: #ffe5ea; + --color-accent-100: #ffccd4; + --color-accent-200: #ff99aa; + --color-accent-300: #ff6680; + --color-accent-400: #ff3355; + --color-accent-500: #ff002b; + --color-accent-600: #cc0022; + --color-accent-700: #99001a; + --color-accent-800: #660011; + --color-accent-900: #330009; + --color-success-25: #ebffeb; + --color-success-50: #c7ffc7; + --color-success-100: #a8ffa8; + --color-success-200: #7aff7a; + --color-success-300: #1aff1a; + --color-success-400: #00e500; + --color-success-500: #00c400; + --color-success-600: #009900; + --color-success-700: #006600; + --color-success-800: #004d00; + --color-success-900: #002900; + --color-neutral-0: #ffffff; + --color-neutral-25: #f7f7f8; + --color-neutral-50: #f1f2f3; + --color-neutral-100: #e5e5e7; + --color-neutral-200: #c9cacf; + --color-neutral-300: #afb0b7; + --color-neutral-400: #94969e; + --color-neutral-500: #747681; + --color-neutral-600: #56575f; + --color-neutral-700: #424349; + --color-neutral-800: #2c2c30; + --color-neutral-900: #18191b; + --color-alpha-white-white-100: #ffffff; + --color-alpha-white-white-80: #ffffffcc; + --color-alpha-white-white-50: #ffffff80; + --color-alpha-white-white-20: #ffffff33; + --color-alpha-white-white-0: #ffffff00; + --color-alpha-black-black-100: #18191b; + --color-alpha-black-black-75: #18191bcc; + --color-alpha-black-black-50: #18191b80; + --color-alpha-black-black-20: #18191b33; + --color-alpha-black-black-0: #18191b00; + --color-alpha-black-black-10: #18191b1a; + --color-alpha-primary-yellow-100: #ffcd14; + --color-alpha-primary-yellow-80: #ffcd14cc; + --color-alpha-primary-yellow-50: #ffcd1480; + --color-alpha-primary-yellow-20: #ffcd1433; + --color-alpha-primary-yellow-0: #ffcd1400; +/* generated-color-tokens:end */ + + /* generated-text-tokens:start */ + --font-family-primary: pretendard; + --font-size-12: 12px; + --font-size-13: 13px; + --font-size-14: 14px; + --font-size-15: 15px; + --font-size-16: 16px; + --font-size-17: 17px; + --font-size-18: 18px; + --line-height-18: 18px; + --font-size-20: 20px; + --line-height-20: 20px; + --font-size-24: 24px; + --line-height-24: 24px; + --font-size-28: 28px; + --line-height-28: 28px; + --font-size-32: 32px; + --font-size-36: 36px; + --line-height-36: 36px; + --font-size-40: 40px; + --font-size-44: 44px; + --font-weight-400: 400; + --font-weight-500: 500; + --font-weight-600: 600; + --font-weight-700: 700; +/* generated-text-tokens:end */ + --radius: 0.625rem; + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); + + /* generated-semantic-tokens:start */ + --color-button-primary-fill: var(--color-primary-400); + --color-button-primary-fill-pressed: var(--color-primary-500); + --color-button-disabled-fill: var(--color-neutral-100); + --color-button-secondary-fill-pressed: var(--color-primary-100); + --color-button-secondary-fill: var(--color-primary-25); + --color-button-secodnary-border: var(--color-primary-400); + --color-button-disabled-border: var(--color-neutral-200); + --color-button-tertiary-border: var(--color-neutral-600); + --color-button-tertiary-fill-pressed: var(--color-neutral-50); + --color-button-tertiary-fill: var(--color-neutral-25); + --color-button-accent-fill: var(--color-accent-500); + --color-button-accent-pressed: var(--color-accent-600); + --color-background-white: var(--color-neutral-0); + --color-background-dim: var(--color-alpha-black-black-75); + --color-background-dim-dark: var(--color-alpha-black-black-20); + --color-background-dim-darker: var(--color-alpha-black-black-50); + --color-background-dim-darkest: var(--color-alpha-black-black-75); + --color-background-brand: var(--color-primary-25); + --color-surface-surface-default: var(--color-neutral-50); + --color-surface-surface-muted: var(--color-neutral-100); + --color-surface-surface-elevated: var(--color-neutral-25); + --color-surface-white: var(--color-neutral-0); + --color-surface-info: var(--color-alpha-black-black-75); + --color-surface-inverse: var(--color-neutral-800); + --color-surface-inverse-default: var(--color-neutral-900); + --color-text-basic: var(--color-neutral-900); + --color-text-subtle: var(--color-neutral-700); + --color-text-disabled: var(--color-neutral-400); + --color-text-basic-inverse: var(--color-neutral-0); + --color-text-subtle-inverse: var(--color-neutral-300); + --color-text-primary: var(--color-primary-900); + --color-text-accent: var(--color-accent-700); + --color-text-success: var(--color-success-700); + --color-text-secondary: var(--color-primary-700); + --color-text-subtler: var(--color-neutral-500); + --color-text-brand: var(--color-primary-400); + --color-text-error: var(--color-accent-500); + --color-icon-inverse: var(--color-neutral-0); + --color-icon-primary: var(--color-primary-400); + --color-icon-subtler: var(--color-neutral-500); + --color-icon-subtle: var(--color-neutral-500); + --color-icon-disabled: var(--color-neutral-400); + --color-icon-basic: var(--color-neutral-700); + --color-icon-gray: var(--color-neutral-200); + --color-icon-secondary: var(--color-primary-700); + --color-icon-tertiary: var(--color-primary-900); + --color-brand-primary: var(--color-primary-400); + --color-element-primary: var(--color-primary-400); + --color-element-primary-light: var(--color-primary-100); + --color-element-primary-lighter: var(--color-primary-50); + --color-element-primary-alpha: var(--color-alpha-primary-yellow-20); + --color-element-gray-subtler: var(--color-neutral-25); + --color-element-gray-subtle: var(--color-neutral-50); + --color-element-gray: var(--color-neutral-100); + --color-element-gray-lighter: var(--color-neutral-25); + --color-element-gray-light: var(--color-neutral-50); + --color-element-gray-dark: var(--color-neutral-200); + --color-element-alpha-light: var(--color-alpha-white-white-80); + --color-element-white: var(--color-neutral-0); + --color-element-alpha-dark: var(--color-alpha-black-black-50); + --color-element-primary-lightest: var(--color-primary-25); + --color-element-gray-darker: var(--color-neutral-400); + --color-element-disabled: var(--color-neutral-100); + --color-element-accent-light: var(--color-accent-300); + --color-element-letter: var(--color-primary-200); + --color-border-primary: var(--color-primary-400); + --color-border-primary-light: var(--color-primary-200); + --color-border-error: var(--color-accent-500); + --color-border-gray: var(--color-neutral-300); + --color-border-gray-light: var(--color-neutral-200); + --color-border-gray-dark: var(--color-neutral-600); + --color-border-gray-darker: var(--color-neutral-800); + --color-border-primary-lighter: var(--color-primary-100); + --color-border-gray-lighter: var(--color-neutral-100); + --color-divider-gray: var(--color-neutral-100); + --color-divider-gray-dark: var(--color-neutral-600); + --color-divider-gray-light: var(--color-neutral-25); + --color-divider-inverse: var(--color-neutral-0); + --color-action-secondary-pressed: var(--color-neutral-100); + --color-action-secondary: var(--color-neutral-0); + --color-action-primary-pressed: var(--color-primary-400); + --color-action-primary: var(--color-primary-25); +/* generated-semantic-tokens:end */ + + /* generated-typography-tokens:start */ + --font-primary: var(--font-family-primary); + --text-12: var(--font-size-12); + --text-13: var(--font-size-13); + --text-14: var(--font-size-14); + --text-15: var(--font-size-15); + --text-16: var(--font-size-16); + --text-17: var(--font-size-17); + --text-18: var(--font-size-18); + --leading-18: var(--line-height-18); + --text-20: var(--font-size-20); + --leading-20: var(--line-height-20); + --text-24: var(--font-size-24); + --leading-24: var(--line-height-24); + --text-28: var(--font-size-28); + --leading-28: var(--line-height-28); + --text-32: var(--font-size-32); + --text-36: var(--font-size-36); + --leading-36: var(--line-height-36); + --text-40: var(--font-size-40); + --text-44: var(--font-size-44); + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); + --color-chart-5: var(--chart-5); + --color-chart-4: var(--chart-4); + --color-chart-3: var(--chart-3); + --color-chart-2: var(--chart-2); + --color-chart-1: var(--chart-1); + --color-ring: var(--ring); + --color-input: var(--input); + --color-border: var(--border); + --color-destructive: var(--destructive); + --color-accent-foreground: var(--accent-foreground); + --color-accent: var(--accent); + --color-muted-foreground: var(--muted-foreground); + --color-muted: var(--muted); + --color-secondary-foreground: var(--secondary-foreground); + --color-secondary: var(--secondary); + --color-primary-foreground: var(--primary-foreground); + --color-primary: var(--primary); + --color-popover-foreground: var(--popover-foreground); + --color-popover: var(--popover); + --color-card-foreground: var(--card-foreground); + --color-card: var(--card); +/* generated-typography-tokens:end */ + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + +body { + font-family: var(--font-pretendard); +} + +/* 모달 애니메이션 */ +@keyframes slide-up { + from { + transform: translateY(100%); + } + to { + transform: translateY(0); + } +} + +.animate-slide-up { + animation: slide-up 0.3s ease-out; +} +/* generated-typography-utilities:start */ +@utility typo-title-lg-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-44); + line-height: var(--line-height-58); +} + +@utility typo-title-lg-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-44); + line-height: var(--line-height-58); +} + +@utility typo-title-md-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-40); + line-height: var(--line-height-52); +} + +@utility typo-title-md-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-40); + line-height: var(--line-height-52); +} + +@utility typo-title-sm-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-36); + line-height: var(--line-height-50); +} + +@utility typo-title-sm-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-36); + line-height: var(--line-height-50); +} + +@utility typo-heading-2xl-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-32); + line-height: var(--line-height-48); +} + +@utility typo-heading-2xl-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-32); + line-height: var(--line-height-48); +} + +@utility typo-heading-2xl-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-32); + line-height: var(--line-height-48); +} + +@utility typo-heading-2xl-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-32); + line-height: var(--line-height-48); +} + +@utility typo-heading-1xl-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-28); + line-height: var(--line-height-42); +} + +@utility typo-heading-1xl-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-28); + line-height: var(--line-height-42); +} + +@utility typo-heading-1xl-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-28); + line-height: var(--line-height-42); +} + +@utility typo-heading-1xl-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-28); + line-height: var(--line-height-42); +} + +@utility typo-heading-lg-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-24); + line-height: var(--line-height-36); +} + +@utility typo-heading-lg-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-24); + line-height: var(--line-height-36); +} + +@utility typo-heading-lg-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-24); + line-height: var(--line-height-36); +} + +@utility typo-heading-lg-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-24); + line-height: var(--line-height-36); +} + +@utility typo-heading-md-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-20); + line-height: var(--line-height-30); +} + +@utility typo-heading-md-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-20); + line-height: var(--line-height-30); +} + +@utility typo-heading-md-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-20); + line-height: var(--line-height-30); +} + +@utility typo-heading-md-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-20); + line-height: var(--line-height-30); +} + +@utility typo-heading-sm-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-18); + line-height: var(--line-height-28); +} + +@utility typo-heading-sm-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-18); + line-height: var(--line-height-28); +} + +@utility typo-heading-sm-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-18); + line-height: var(--line-height-28); +} + +@utility typo-heading-sm-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-18); + line-height: var(--line-height-28); +} + +@utility typo-body-1xl-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-17); + line-height: var(--line-height-26); +} + +@utility typo-body-1xl-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-17); + line-height: var(--line-height-26); +} + +@utility typo-body-1xl-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-17); + line-height: var(--line-height-26); +} + +@utility typo-body-1xl-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-17); + line-height: var(--line-height-26); +} + +@utility typo-body-lg-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-16); + line-height: var(--line-height-24); +} + +@utility typo-body-lg-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-16); + line-height: var(--line-height-24); +} + +@utility typo-body-lg-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-16); + line-height: var(--line-height-24); +} + +@utility typo-body-lg-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-16); + line-height: var(--line-height-24); +} + +@utility typo-body-md-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-15); + line-height: var(--line-height-24); +} + +@utility typo-body-md-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-15); + line-height: var(--line-height-24); +} + +@utility typo-body-md-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-15); + line-height: var(--line-height-24); +} + +@utility typo-body-md-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-15); + line-height: var(--line-height-24); +} + +@utility typo-body-sm-bold { + font-family: var(--font-primary); + font-weight: 700; + font-size: var(--font-size-14); + line-height: var(--line-height-20); +} + +@utility typo-body-sm-semibold { + font-family: var(--font-primary); + font-weight: 600; + font-size: var(--font-size-14); + line-height: var(--line-height-20); +} + +@utility typo-body-sm-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-14); + line-height: var(--line-height-20); +} + +@utility typo-body-sm-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-14); + line-height: var(--line-height-20); +} + +@utility typo-caption-md-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-13); + line-height: var(--line-height-20); +} + +@utility typo-caption-md-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-13); + line-height: var(--line-height-20); +} + +@utility typo-caption-sm-medium { + font-family: var(--font-primary); + font-weight: 500; + font-size: var(--font-size-12); + line-height: var(--line-height-18); +} + +@utility typo-caption-sm-regular { + font-family: var(--font-primary); + font-weight: 400; + font-size: var(--font-size-12); + line-height: var(--line-height-18); +} + +@utility drop-shadow-25-5 { + box-shadow: 0px 0px 25px 5px #00000014; +} + +@utility drop-shadow-10-5 { + box-shadow: 0px 0px 10px 5px #0000000d; +} + +/* generated-typography-utilities:end */ + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.205 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.205 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.922 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.704 0.191 22.216); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.556 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.556 0 0); +} + +@layer base { + a { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0.1); /* a tag 누르면 살짝 회색 배경 보이게 */ + } + + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } +} + +@keyframes marquee { + from { + transform: translateX(0); + } + to { + transform: translateX(-50%); + } +} + +.tab-highlight { + -webkit-tap-highlight-color: rgba(0, 0, 0, 0.1); +} + +.animate-marquee { + animation-name: marquee; + animation-timing-function: linear; + animation-iteration-count: infinite; +} + +.scrollbar-hide::-webkit-scrollbar { + display: none; /* Chrome, Safari, Opera */ +} +.scrollbar-hide { + -ms-overflow-style: none; /* IE, Edge */ + scrollbar-width: none; /* Firefox */ +} + +@supports (-webkit-touch-callout: none) { + .h-screen { + height: -webkit-fill-available; + } + .min-h-screen { + min-height: -webkit-fill-available; + } + .max-h-screen { + max-height: -webkit-fill-available; + } +} + diff --git a/src/app/image-test/page.tsx b/src/app/image-test/page.tsx new file mode 100644 index 00000000..3c3b079f --- /dev/null +++ b/src/app/image-test/page.tsx @@ -0,0 +1,244 @@ +'use client'; + +import exifr from 'exifr'; // ★ 추가 +import { ChangeEvent, SyntheticEvent, useEffect, useState } from 'react'; + +function generateUUID() { + if (window.crypto?.randomUUID) { + return window.crypto.randomUUID(); + } + // fallback + return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => { + const r = (Math.random() * 16) | 0; + const v = c === 'x' ? r : (r & 0x3) | 0x8; + return v.toString(16); + }); +} + +const FILE_SIZE = 2000; + +type PreviewItem = { + id: string; + name: string; + size: number; + type: string; + url: string; // object URL + pickedAt: number; // URL 만든 시점 (performance.now) - 디코드 시간 계산용 + createdAt: number; // EXIF(또는 lastModified) 기반 "이미지 생성/촬영 시각" + createdAtSource: 'exif' | 'file'; // 출처 + loadedAt?: number; // onLoad 시점 (performance.now) + width?: number; + height?: number; +}; + +function formatBytes(n: number) { + const u = ['B', 'KB', 'MB', 'GB']; + let i = 0; + let v = n; + while (v >= 1024 && i < u.length - 1) { + v /= 1024; + i++; + } + return `${v.toFixed(1)} ${u[i]}`; +} + +function formatDate(ts: number) { + const d = new Date(ts); + // 예: 2025-10-29 11:22:33 + const pad = (x: number) => String(x).padStart(2, '0'); + return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())} ${pad(d.getHours())}:${pad(d.getMinutes())}:${pad(d.getSeconds())}`; +} + +// EXIF에서 생성시각(촬영시각)을 가져오되, 없으면 file.lastModified로 폴백 +async function getImageCreatedMs( + file: File, +): Promise<{ ts: number; source: 'exif' | 'file' }> { + try { + const tags = await exifr.parse(file, { + pick: ['DateTimeOriginal', 'CreateDate', 'ModifyDate'], + translateValues: true, + }); + + // 우선순위: DateTimeOriginal > CreateDate > ModifyDate + const dt: Date | undefined = + (tags?.DateTimeOriginal as Date | undefined) ?? + (tags?.CreateDate as Date | undefined) ?? + (tags?.ModifyDate as Date | undefined); + + if (dt && typeof dt.getTime === 'function') { + const ms = dt.getTime(); + if (!Number.isNaN(ms)) return { ts: ms, source: 'exif' }; + } + } catch {} + // 스크린샷/편집본/소셜 다운로드 등 EXIF 없음 → 파일 시스템 타임스탬프 사용 + return { ts: file.lastModified, source: 'file' }; +} + +export default function UploadPreview100() { + const [items, setItems] = useState([]); + const [limit, setLimit] = useState(FILE_SIZE); + const [loadingCount, setLoadingCount] = useState(0); + + useEffect(() => { + return () => { + items.forEach((it) => URL.revokeObjectURL(it.url)); + }; + }, []); + + async function handlePick(e: ChangeEvent) { + const fl = e.target.files; + if (!fl) return; + + const files = Array.from(fl) + .filter((f) => f.type.startsWith('image/')) + .slice(0, limit); + + // 이전 미리보기 정리 + setItems((prev) => { + prev.forEach((p) => URL.revokeObjectURL(p.url)); + return []; + }); + + // EXIF 읽기 + 프리뷰 구성 병렬 처리 + const next: PreviewItem[] = await Promise.all( + files.map(async (f) => { + const { ts, source } = await getImageCreatedMs(f); + return { + id: `${f.name}-${generateUUID()}`, // 중복 파일명 대비 + name: f.name, + size: f.size, + type: f.type, + url: URL.createObjectURL(f), + pickedAt: performance.now(), + createdAt: ts, + createdAtSource: source, + }; + }), + ); + + setItems(next); + setLoadingCount(next.length); + } + + function onImgLoad(i: number, ev: SyntheticEvent) { + const el = ev.currentTarget; + const loadedAt = performance.now(); + setItems((prev) => { + const cp = [...prev]; + const it = cp[i]; + if (!it) return prev; + cp[i] = { + ...it, + loadedAt, + width: el.naturalWidth, + height: el.naturalHeight, + }; + return cp; + }); + setLoadingCount((c) => Math.max(0, c - 1)); + } + + const totalBytes = items.reduce((a, b) => a + b.size, 0); + const loadedItems = items.filter((i) => i.loadedAt); + const avgDecodeMs = loadedItems.length + ? Math.round( + loadedItems.reduce( + (a, b) => a + Math.max(0, b.loadedAt! - b.pickedAt), + 0, + ) / loadedItems.length, + ) + : 0; + + return ( +
+

+ 이미지 {FILE_SIZE}장 동시 업로드(미리보기) 테스트 +

+ +
+
+
+ + setLimit(Number(e.target.value))} + className='w-full rounded-xl border px-3 py-2' + /> +
+
+ + +
+
+ +
+
+ 총 개수: {items.length} +
+ {/*
로딩 중 개수: {loadingCount}
*/} +
+ 총 용량: {formatBytes(totalBytes)} +
+
+ 평균 디코드: {avgDecodeMs} ms +
+
+
+ + {/* Grid */} +
+ {items.map((it, i) => ( +
+ {it.name} onImgLoad(i, e)} + className='max-h-[320px] w-full rounded-lg object-cover' + /> +
+
+ {it.name} +
+
+ + {formatBytes(it.size)} · {it.width ?? '-'}×{it.height ?? '-'} + + + 생성시각:{' '} + {formatDate(it.createdAt)} + + ({it.createdAtSource === 'exif' ? 'EXIF' : '파일시스템'}) + + + {/* 디코드 시간 보려면 주석 해제 + {it.loadedAt && ( + {Math.max(0, Math.round(it.loadedAt - it.pickedAt))} ms + )} */} +
+
+
+ ))} +
+
+ ); +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 00000000..5a4309b9 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,109 @@ +import KakaoProvider from '@/global/context/KakaoProvider'; +import QueryProvider from '@/global/context/QueryProvider'; +import type { Metadata } from 'next'; +import localFont from 'next/font/local'; +import './globals.css'; + +const pretendard = localFont({ + src: '../../public/fonts/PretendardVariable.woff2', + display: 'swap', + weight: '100 900', + variable: '--font-pretendard', + preload: false, +}); + +export const metadata: Metadata = { + title: '치이이즈: 추억은 따끈할 때 제맛', + description: '딱 7일만 열리는 특별한 공유 앨범 서비스', + metadataBase: new URL('https://say-cheese.me'), + openGraph: { + title: '치이이즈: 추억은 따끈할 때 제맛', + description: '딱 7일만 열리는 특별한 공유 앨범 서비스', + url: 'https://say-cheese.me/main', + siteName: '치이이즈', + images: [ + { + url: '/og/default_og.png', + width: 1200, + height: 630, + alt: '치이이즈', + }, + ], + locale: 'ko_KR', + type: 'website', + }, +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + */} + + +
+
+ + {children} + +
+
+ + + ); +} diff --git a/src/app/login/page.tsx b/src/app/login/page.tsx new file mode 100644 index 00000000..65469ca3 --- /dev/null +++ b/src/app/login/page.tsx @@ -0,0 +1,48 @@ +import KakaoSignupButton from '@/feature/login/components/KakaoSignupButton'; +import Image from 'next/image'; +import { Suspense } from 'react'; + +export default function LoginPage() { + return ( +
+ {/* */} +
+ 치즈 아이콘 + 치즈 아이콘 + + 우리가 특별한 순간을 기억하는 법 + +
+ +
+
+
+ + ⚡️3초만에 빠른 회원가입 + +
+ 삼각형 +
+ + + + +
+
+ ); +} diff --git a/src/app/main/closed-album/page.tsx b/src/app/main/closed-album/page.tsx new file mode 100644 index 00000000..91ec5566 --- /dev/null +++ b/src/app/main/closed-album/page.tsx @@ -0,0 +1,7 @@ +import ScreenMainClosedAlbum from '@/feature/main/closed-album/components/ScreenMainClosedAlbum'; + +interface PageProps {} + +export default function Page({}: PageProps) { + return ; +} diff --git a/src/app/main/page.tsx b/src/app/main/page.tsx new file mode 100644 index 00000000..26fc4dbd --- /dev/null +++ b/src/app/main/page.tsx @@ -0,0 +1,7 @@ +import ScreenMain from '@/feature/main/components/ScreenMain'; + +interface MainPageProps {} + +export default function MainPage({}: MainPageProps) { + return ; +} diff --git a/src/app/mypage/setting/page.tsx b/src/app/mypage/setting/page.tsx new file mode 100644 index 00000000..f06aec82 --- /dev/null +++ b/src/app/mypage/setting/page.tsx @@ -0,0 +1,11 @@ +import ScreenMypage from '@/feature/mypage/components/ScreenMypage'; + +interface PageProps {} + +export default function Page({}: PageProps) { + return ( + <> + + + ); +} diff --git a/src/app/oauth/callback/route.ts b/src/app/oauth/callback/route.ts new file mode 100644 index 00000000..133d7403 --- /dev/null +++ b/src/app/oauth/callback/route.ts @@ -0,0 +1,110 @@ +import { + ACCESS_TOKEN_KEY, + REFRESH_TOKEN_KEY, +} from '@/global/constants/cookies'; +import { NextRequest, NextResponse } from 'next/server'; + +export async function GET(request: NextRequest) { + const { searchParams } = new URL(request.url); + const protocol = request.headers.get('x-forwarded-proto') || 'https'; + const host = + request.headers.get('x-forwarded-host') || + request.headers.get('host') || + 'localhost:3000'; + const code = searchParams.get('code'); + const redirectParam = searchParams.get('redirect'); + let redirect: string | null = null; + + if (redirectParam) { + const decodedRedirect = decodeURIComponent(redirectParam); + try { + redirect = new URL(decodedRedirect, `${protocol}://${host}`).toString(); + } catch { + redirect = null; + } + } + + if (!code) { + return NextResponse.json( + { error: 'Authorization code is required' }, + { status: 400 }, + ); + } + + try { + const response = await fetch( + `${process.env.NEXT_PUBLIC_API_URL}/v1/auth/exchange?code=${code}`, + { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }, + ); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`요청 실패: ${response.status} ${errorText}`); + } + + const data = await response.json(); + + // ----- entry 쿠키 기준 서버 분기 ----- + const cookieDomain = + process.env.NODE_ENV === 'production' ? '.say-cheese.me' : undefined; + + // 쿠키 파싱 (Next.js 13+ 방식) + const entry = request.cookies.get('entry')?.value ?? null; + + let redirectPath = '/main'; + if (data.result.isOnboarded) { + if (entry === 'create-album') { + redirectPath = '/create-album'; + } + } else { + redirectPath = '/onboarding'; + } + const redirectUrl = new URL(redirectPath, `${protocol}://${host}`); + + if (!data.result.isOnboarded) { + redirectUrl.searchParams.set( + 'onboarding', + data.result.isOnboarded.toString(), + ); + redirectUrl.searchParams.set( + 'name', + encodeURIComponent(data.result.name), + ); + } + + // redirect 응답 객체 생성 + const res = NextResponse.redirect(redirect || redirectUrl); + res.cookies.set(ACCESS_TOKEN_KEY, data.result.accessToken, { + secure: process.env.NODE_ENV === 'production', + sameSite: 'lax', + domain: cookieDomain, + maxAge: 60 * 60 * 2, // 2시간 + path: '/', + }); + res.cookies.set(REFRESH_TOKEN_KEY, data.result.refreshToken, { + secure: process.env.NODE_ENV === 'production', + domain: cookieDomain, + sameSite: 'lax', + maxAge: 60 * 60 * 24 * 7, // 7일 + path: '/', + }); + // entry 쿠키 삭제 (만료일을 과거로) + res.cookies.set('entry', '', { + path: '/', + expires: new Date(0), + domain: cookieDomain, + }); + return res; + } catch (error) { + console.error('Auth callback error:', error); + return NextResponse.json( + { error: '서버사이드에서 토큰 처리 중 오류 발생' }, + { status: 500 }, + ); + } +} diff --git a/src/app/onboarding/complete/page.tsx b/src/app/onboarding/complete/page.tsx new file mode 100644 index 00000000..2651d4ea --- /dev/null +++ b/src/app/onboarding/complete/page.tsx @@ -0,0 +1,5 @@ +import ScreenOnboardingComplete from '@/feature/onboarding/components/ScreenOnBoardingComplete'; + +export default function page() { + return ; +} diff --git a/src/app/onboarding/page.tsx b/src/app/onboarding/page.tsx new file mode 100644 index 00000000..1674158a --- /dev/null +++ b/src/app/onboarding/page.tsx @@ -0,0 +1,11 @@ +import ScreenOnBoarding from '@/feature/onboarding/components/ScreenOnBoarding'; +import Spinner from '@/global/components/Spinner'; +import { Suspense } from 'react'; + +export default function OnBoardingPage() { + return ( + }> + + + ); +} diff --git a/src/app/page.tsx b/src/app/page.tsx new file mode 100644 index 00000000..33cd9e1a --- /dev/null +++ b/src/app/page.tsx @@ -0,0 +1,5 @@ +import ScreenRoot from '@/feature/root/components/ScreenRoot'; + +export default function Page() { + return ; +} diff --git a/src/app/photo/detail/[albumId]/page.tsx b/src/app/photo/detail/[albumId]/page.tsx new file mode 100644 index 00000000..5083f286 --- /dev/null +++ b/src/app/photo/detail/[albumId]/page.tsx @@ -0,0 +1,11 @@ +import ScreenPhotoDetail from '@/feature/photo-detail/components/ScreenPhotoDetail'; + +export default async function Page({ + params, +}: { + params: Promise<{ albumId: string }>; +}) { + const { albumId } = await params; + + return ; +} diff --git a/src/app/photo/entry/[albumId]/page.tsx b/src/app/photo/entry/[albumId]/page.tsx new file mode 100644 index 00000000..5362c84b --- /dev/null +++ b/src/app/photo/entry/[albumId]/page.tsx @@ -0,0 +1,12 @@ +import ScreenPhotoShareEntry from '@/feature/photo-entry/components/ScreenPhotoShareEntry'; +import { Suspense } from 'react'; + +export default function Page({ params }: { params: { albumId: string } }) { + const { albumId } = params; + + return ( + + + + ); +} diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts new file mode 100644 index 00000000..800d9e9c --- /dev/null +++ b/src/app/sitemap.ts @@ -0,0 +1,32 @@ +import { MetadataRoute } from 'next'; + +export default function sitemap(): MetadataRoute.Sitemap { + const baseUrl = 'https://say-cheese.me'; + + return [ + { + url: baseUrl, + lastModified: new Date(), + changeFrequency: 'yearly', + priority: 1, + }, + { + url: `${baseUrl}/main`, + lastModified: new Date(), + changeFrequency: 'daily', + priority: 0.9, + }, + { + url: `${baseUrl}/login`, + lastModified: new Date(), + changeFrequency: 'monthly', + priority: 0.5, + }, + { + url: `${baseUrl}/create-album`, + lastModified: new Date(), + changeFrequency: 'monthly', + priority: 0.8, + }, + ]; +} diff --git a/src/app/term/page.tsx b/src/app/term/page.tsx new file mode 100644 index 00000000..fdda304d --- /dev/null +++ b/src/app/term/page.tsx @@ -0,0 +1,11 @@ +import ScreenTerm from '@/feature/term/ScreenTerm'; +import Spinner from '@/global/components/Spinner'; +import { Suspense } from 'react'; + +export default function TermPage() { + return ( + }> + + + ); +} diff --git a/src/app/token.json b/src/app/token.json new file mode 100644 index 00000000..8c83e003 --- /dev/null +++ b/src/app/token.json @@ -0,0 +1,1924 @@ +{ + "global": { + "primary": { + "25": { + "value": "#fffbeb", + "type": "color" + }, + "50": { + "value": "#fff7d6", + "type": "color" + }, + "100": { + "value": "#fff2c2", + "type": "color" + }, + "200": { + "value": "#ffe894", + "type": "color" + }, + "300": { + "value": "#ffdc5c", + "type": "color" + }, + "400": { + "value": "#ffcd14", + "type": "color" + }, + "500": { + "value": "#ffb700", + "type": "color" + }, + "600": { + "value": "#d18800", + "type": "color" + }, + "700": { + "value": "#996300", + "type": "color" + }, + "800": { + "value": "#664200", + "type": "color" + }, + "900": { + "value": "#332100", + "type": "color" + } + }, + "gray": { + "25": { + "value": "#f8f8f7", + "type": "color" + }, + "50": { + "value": "#f3f3f1", + "type": "color" + }, + "100": { + "value": "#e7e6e4", + "type": "color" + }, + "200": { + "value": "#cfcec9", + "type": "color" + }, + "300": { + "value": "#b7b5ae", + "type": "color" + }, + "400": { + "value": "#9f9d93", + "type": "color" + }, + "500": { + "value": "#827f73", + "type": "color" + }, + "600": { + "value": "#605d55", + "type": "color" + }, + "700": { + "value": "#494741", + "type": "color" + }, + "800": { + "value": "#31302b", + "type": "color" + }, + "900": { + "value": "#1b1a18", + "type": "color" + } + }, + "dropshadow-25-5": { + "value": { + "color": "#00000014", + "type": "dropShadow", + "x": 0, + "y": 0, + "blur": 25, + "spread": 5 + }, + "type": "boxShadow" + }, + "dropshadow-10-5": { + "value": { + "color": "#0000000d", + "type": "dropShadow", + "x": 0, + "y": 0, + "blur": 10, + "spread": 5 + }, + "type": "boxShadow" + }, + "fontWeights": { + "pretendard-0": { + "value": "Bold", + "type": "fontWeights" + }, + "pretendard-1": { + "value": "SemiBold", + "type": "fontWeights" + }, + "pretendard-2": { + "value": "Medium", + "type": "fontWeights" + }, + "pretendard-3": { + "value": "Regular", + "type": "fontWeights" + } + }, + "fontSize": { + "0": { + "value": 12, + "type": "fontSizes" + }, + "1": { + "value": 13, + "type": "fontSizes" + }, + "2": { + "value": 14, + "type": "fontSizes" + }, + "3": { + "value": 15, + "type": "fontSizes" + }, + "4": { + "value": 16, + "type": "fontSizes" + }, + "5": { + "value": 17, + "type": "fontSizes" + }, + "6": { + "value": 18, + "type": "fontSizes" + }, + "7": { + "value": 20, + "type": "fontSizes" + }, + "8": { + "value": 24, + "type": "fontSizes" + }, + "9": { + "value": 28, + "type": "fontSizes" + }, + "10": { + "value": 32, + "type": "fontSizes" + }, + "11": { + "value": 36, + "type": "fontSizes" + }, + "12": { + "value": 40, + "type": "fontSizes" + }, + "13": { + "value": 44, + "type": "fontSizes" + } + }, + "letterSpacing": { + "0": { + "value": 0, + "type": "letterSpacing" + } + }, + "paragraphSpacing": { + "0": { + "value": 0, + "type": "paragraphSpacing" + } + }, + "title": { + "lg": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.58}", + "fontSize": "{fontSize.13}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.58}", + "fontSize": "{fontSize.13}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "md": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.52}", + "fontSize": "{fontSize.12}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.52}", + "fontSize": "{fontSize.12}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "sm": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.50}", + "fontSize": "{fontSize.11}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.50}", + "fontSize": "{fontSize.11}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + } + }, + "heading": { + "2xl": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.48}", + "fontSize": "{fontSize.10}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.48}", + "fontSize": "{fontSize.10}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.48}", + "fontSize": "{fontSize.10}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.48}", + "fontSize": "{fontSize.10}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "1xl": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.42}", + "fontSize": "{fontSize.9}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.42}", + "fontSize": "{fontSize.9}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.42}", + "fontSize": "{fontSize.9}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.42}", + "fontSize": "{fontSize.9}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "lg": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.36}", + "fontSize": "{fontSize.8}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.36}", + "fontSize": "{fontSize.8}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.36}", + "fontSize": "{fontSize.8}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.36}", + "fontSize": "{fontSize.8}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "md": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.30}", + "fontSize": "{fontSize.7}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.30}", + "fontSize": "{fontSize.7}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.30}", + "fontSize": "{fontSize.7}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.30}", + "fontSize": "{fontSize.7}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "sm": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.28}", + "fontSize": "{fontSize.6}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.28}", + "fontSize": "{fontSize.6}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.28}", + "fontSize": "{fontSize.6}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.28}", + "fontSize": "{fontSize.6}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + } + }, + "body": { + "1xl": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.26}", + "fontSize": "{fontSize.5}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.26}", + "fontSize": "{fontSize.5}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.26}", + "fontSize": "{fontSize.5}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.26}", + "fontSize": "{fontSize.5}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "lg": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.4}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.4}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.4}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.4}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "md": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.3}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.3}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.3}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.24}", + "fontSize": "{fontSize.3}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "sm": { + "bold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-0}", + "lineHeight": "{font.line height.20}", + "fontSize": "{fontSize.2}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "semibold": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-1}", + "lineHeight": "{font.line height.20}", + "fontSize": "{fontSize.2}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.20}", + "fontSize": "{fontSize.2}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.20}", + "fontSize": "{fontSize.2}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + } + }, + "caption": { + "md": { + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.20}", + "fontSize": "{fontSize.1}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.20}", + "fontSize": "{fontSize.1}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + }, + "sm": { + "medium": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-2}", + "lineHeight": "{font.line height.18}", + "fontSize": "{fontSize.0}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + }, + "regular": { + "value": { + "fontFamily": "{font.family.pretendard}", + "fontWeight": "{fontWeights.pretendard-3}", + "lineHeight": "{font.line height.18}", + "fontSize": "{fontSize.0}", + "letterSpacing": "{letterSpacing.0}", + "paragraphSpacing": "{paragraphSpacing.0}", + "paragraphIndent": "{paragraphIndent.0}", + "textCase": "{textCase.none}", + "textDecoration": "{textDecoration.none}" + }, + "type": "typography" + } + } + }, + "textCase": { + "none": { + "value": "none", + "type": "textCase" + } + }, + "textDecoration": { + "none": { + "value": "none", + "type": "textDecoration" + } + }, + "paragraphIndent": { + "0": { + "value": "0px", + "type": "dimension" + } + } + }, + "primitive/Mode 1": { + "color": { + "primary": { + "25": { + "value": "#fffbeb", + "type": "color" + }, + "50": { + "value": "#fff7d6", + "type": "color" + }, + "100": { + "value": "#fff2c2", + "type": "color" + }, + "200": { + "value": "#ffe894", + "type": "color" + }, + "300": { + "value": "#ffdc5c", + "type": "color" + }, + "400": { + "value": "#ffcd14", + "type": "color" + }, + "500": { + "value": "#ffb700", + "type": "color" + }, + "600": { + "value": "#e09900", + "type": "color" + }, + "700": { + "value": "#996300", + "type": "color" + }, + "800": { + "value": "#664200", + "type": "color" + }, + "900": { + "value": "#332100", + "type": "color" + } + }, + "accent": { + "25": { + "value": "#fff5f6", + "type": "color" + }, + "50": { + "value": "#ffe5ea", + "type": "color" + }, + "100": { + "value": "#ffccd4", + "type": "color" + }, + "200": { + "value": "#ff99aa", + "type": "color" + }, + "300": { + "value": "#ff6680", + "type": "color" + }, + "400": { + "value": "#ff3355", + "type": "color" + }, + "500": { + "value": "#ff002b", + "type": "color" + }, + "600": { + "value": "#cc0022", + "type": "color" + }, + "700": { + "value": "#99001a", + "type": "color" + }, + "800": { + "value": "#660011", + "type": "color" + }, + "900": { + "value": "#330009", + "type": "color" + } + }, + "success": { + "25": { + "value": "#ebffeb", + "type": "color" + }, + "50": { + "value": "#c7ffc7", + "type": "color" + }, + "100": { + "value": "#a8ffa8", + "type": "color" + }, + "200": { + "value": "#7aff7a", + "type": "color" + }, + "300": { + "value": "#1aff1a", + "type": "color" + }, + "400": { + "value": "#00e500", + "type": "color" + }, + "500": { + "value": "#00c400", + "type": "color" + }, + "600": { + "value": "#009900", + "type": "color" + }, + "700": { + "value": "#006600", + "type": "color" + }, + "800": { + "value": "#004d00", + "type": "color" + }, + "900": { + "value": "#002900", + "type": "color" + } + }, + "neutral": { + "0": { + "value": "#ffffff", + "type": "color" + }, + "25": { + "value": "#f7f7f8", + "type": "color" + }, + "50": { + "value": "#f1f2f3", + "type": "color" + }, + "100": { + "value": "#e5e5e7", + "type": "color" + }, + "200": { + "value": "#c9cacf", + "type": "color" + }, + "300": { + "value": "#afb0b7", + "type": "color" + }, + "400": { + "value": "#94969e", + "type": "color" + }, + "500": { + "value": "#747681", + "type": "color" + }, + "600": { + "value": "#56575f", + "type": "color" + }, + "700": { + "value": "#424349", + "type": "color" + }, + "800": { + "value": "#2c2c30", + "type": "color" + }, + "900": { + "value": "#18191b", + "type": "color" + } + }, + "alpha": { + "white": { + "white 100%": { + "value": "#ffffff", + "type": "color" + }, + "white 80%": { + "value": "#ffffffcc", + "type": "color" + }, + "white 50%": { + "value": "#ffffff80", + "type": "color" + }, + "white 20%": { + "value": "#ffffff33", + "type": "color" + }, + "white 0%": { + "value": "#ffffff00", + "type": "color" + } + }, + "black": { + "black 100%": { + "value": "#18191b", + "type": "color" + }, + "black 75%": { + "value": "#18191bcc", + "type": "color" + }, + "black 50%": { + "value": "#18191b80", + "type": "color" + }, + "black 20%": { + "value": "#18191b33", + "type": "color" + }, + "black 0%": { + "value": "#18191b00", + "type": "color" + }, + "black 10%": { + "value": "#18191b1a", + "type": "color" + } + }, + "primary": { + "yellow 100%": { + "value": "#ffcd14", + "type": "color" + }, + "yellow 80%": { + "value": "#ffcd14cc", + "type": "color" + }, + "yellow 50%": { + "value": "#ffcd1480", + "type": "color" + }, + "yellow 20%": { + "value": "#ffcd1433", + "type": "color" + }, + "yellow 0%": { + "value": "#ffcd1400", + "type": "color" + } + } + } + }, + "font": { + "family": { + "pretendard": { + "value": "pretendard", + "type": "text" + } + }, + "size": { + "12": { + "value": 12, + "type": "number" + }, + "13": { + "value": 13, + "type": "number" + }, + "14": { + "value": 14, + "type": "number" + }, + "15": { + "value": 15, + "type": "number" + }, + "16": { + "value": 16, + "type": "number" + }, + "17": { + "value": 17, + "type": "number" + }, + "18": { + "value": 18, + "type": "number" + }, + "20": { + "value": 20, + "type": "number" + }, + "24": { + "value": 24, + "type": "number" + }, + "28": { + "value": 28, + "type": "number" + }, + "32": { + "value": 32, + "type": "number" + }, + "36": { + "value": 36, + "type": "number" + }, + "40": { + "value": 40, + "type": "number" + }, + "44": { + "value": 44, + "type": "number" + } + }, + "line height": { + "18": { + "value": 18, + "type": "number" + }, + "20": { + "value": 20, + "type": "number" + }, + "24": { + "value": 24, + "type": "number" + }, + "26": { + "value": 26, + "type": "number" + }, + "28": { + "value": 28, + "type": "number" + }, + "30": { + "value": 30, + "type": "number" + }, + "36": { + "value": 36, + "type": "number" + }, + "42": { + "value": 42, + "type": "number" + }, + "48": { + "value": 48, + "type": "number" + }, + "50": { + "value": 50, + "type": "number" + }, + "52": { + "value": 52, + "type": "number" + }, + "58": { + "value": 58, + "type": "number" + } + }, + "weight": { + "400": { + "value": 400, + "type": "number" + }, + "500": { + "value": 500, + "type": "number" + }, + "600": { + "value": 600, + "type": "number" + }, + "700": { + "value": 700, + "type": "number" + } + } + }, + "number": { + "0": { + "value": 0, + "type": "number" + }, + "1": { + "value": 1, + "type": "number" + }, + "2": { + "value": 2, + "type": "number" + }, + "3": { + "value": 4, + "type": "number" + }, + "4": { + "value": 6, + "type": "number" + }, + "5": { + "value": 8, + "type": "number" + }, + "6": { + "value": 10, + "type": "number" + }, + "7": { + "value": 12, + "type": "number" + }, + "8": { + "value": 16, + "type": "number" + }, + "9": { + "value": 20, + "type": "number" + }, + "10": { + "value": 24, + "type": "number" + }, + "11": { + "value": 28, + "type": "number" + }, + "12": { + "value": 32, + "type": "number" + }, + "13": { + "value": 36, + "type": "number" + }, + "14": { + "value": 40, + "type": "number" + }, + "15": { + "value": 44, + "type": "number" + }, + "16": { + "value": 48, + "type": "number" + }, + "17": { + "value": 56, + "type": "number" + }, + "18": { + "value": 64, + "type": "number" + }, + "19": { + "value": 72, + "type": "number" + }, + "20": { + "value": 80, + "type": "number" + }, + "21": { + "value": 96, + "type": "number" + }, + "max": { + "value": 1000, + "type": "number" + } + } + }, + "semantic/Mode 1": { + "color": { + "button": { + "primary-fill": { + "value": "{color.primary.400}", + "type": "color" + }, + "primary-fill-pressed": { + "value": "{color.primary.500}", + "type": "color" + }, + "disabled-fill": { + "value": "{color.neutral.100}", + "type": "color" + }, + "secondary-fill-pressed": { + "value": "{color.primary.100}", + "type": "color" + }, + "secondary-fill": { + "value": "{color.primary.25}", + "type": "color" + }, + "secodnary-border": { + "value": "{color.primary.400}", + "type": "color" + }, + "disabled-border": { + "value": "{color.neutral.200}", + "type": "color" + }, + "tertiary-border": { + "value": "{color.neutral.600}", + "type": "color" + }, + "tertiary-fill-pressed": { + "value": "{color.neutral.50}", + "type": "color" + }, + "tertiary-fill": { + "value": "{color.neutral.25}", + "type": "color" + }, + "accent-fill": { + "value": "{color.accent.500}", + "type": "color" + }, + "accent-pressed": { + "value": "{color.accent.600}", + "type": "color" + } + }, + "background": { + "white": { + "value": "{color.neutral.0}", + "type": "color" + }, + "dim": { + "value": "{color.alpha.black.black 75%}", + "type": "color" + }, + "dim-dark": { + "value": "{color.alpha.black.black 20%}", + "type": "color" + }, + "dim-darker": { + "value": "{color.alpha.black.black 50%}", + "type": "color" + }, + "dim-darkest": { + "value": "{color.alpha.black.black 75%}", + "type": "color" + }, + "brand": { + "value": "{color.primary.25}", + "type": "color" + } + }, + "surface": { + "surface-default": { + "value": "{color.neutral.50}", + "type": "color" + }, + "surface-muted": { + "value": "{color.neutral.100}", + "type": "color" + }, + "surface-elevated": { + "value": "{color.neutral.25}", + "type": "color" + }, + "white": { + "value": "{color.neutral.0}", + "type": "color" + }, + "info": { + "value": "{color.alpha.black.black 75%}", + "type": "color" + }, + "inverse": { + "value": "{color.neutral.800}", + "type": "color" + }, + "inverse-default": { + "value": "{color.neutral.900}", + "type": "color" + } + }, + "text": { + "basic": { + "value": "{color.neutral.900}", + "type": "color" + }, + "subtle": { + "value": "{color.neutral.700}", + "type": "color" + }, + "disabled": { + "value": "{color.neutral.400}", + "type": "color" + }, + "basic-inverse": { + "value": "{color.neutral.0}", + "type": "color" + }, + "subtle-inverse": { + "value": "{color.neutral.300}", + "type": "color" + }, + "primary": { + "value": "{color.primary.900}", + "type": "color" + }, + "accent": { + "value": "{color.accent.700}", + "type": "color" + }, + "success": { + "value": "{color.success.700}", + "type": "color" + }, + "secondary": { + "value": "{color.primary.700}", + "type": "color" + }, + "subtler": { + "value": "{color.neutral.500}", + "type": "color" + }, + "brand": { + "value": "{color.primary.400}", + "type": "color" + }, + "error": { + "value": "{color.accent.500}", + "type": "color" + } + }, + "icon": { + "inverse": { + "value": "{color.neutral.0}", + "type": "color" + }, + "primary": { + "value": "{color.primary.400}", + "type": "color" + }, + "subtler": { + "value": "{color.neutral.500}", + "type": "color" + }, + "subtle": { + "value": "{color.neutral.500}", + "type": "color" + }, + "disabled": { + "value": "{color.neutral.400}", + "type": "color" + }, + "basic": { + "value": "{color.neutral.700}", + "type": "color" + }, + "gray": { + "value": "{color.neutral.200}", + "type": "color" + }, + "secondary": { + "value": "{color.primary.700}", + "type": "color" + }, + "tertiary": { + "value": "{color.primary.900}", + "type": "color" + } + }, + "brand": { + "primary": { + "value": "{color.primary.400}", + "type": "color" + } + }, + "element": { + "primary": { + "value": "{color.primary.400}", + "type": "color" + }, + "primary-light": { + "value": "{color.primary.100}", + "type": "color" + }, + "primary-lighter": { + "value": "{color.primary.50}", + "type": "color" + }, + "primary-alpha": { + "value": "{color.alpha.primary.yellow 20%}", + "type": "color" + }, + "gray-subtler": { + "value": "{color.neutral.25}", + "type": "color" + }, + "gray-subtle": { + "value": "{color.neutral.50}", + "type": "color" + }, + "gray": { + "value": "{color.neutral.100}", + "type": "color" + }, + "gray-lighter": { + "value": "{color.neutral.25}", + "type": "color" + }, + "gray-light": { + "value": "{color.neutral.50}", + "type": "color" + }, + "gray-dark": { + "value": "{color.neutral.200}", + "type": "color" + }, + "alpha-light": { + "value": "{color.alpha.white.white 80%}", + "type": "color" + }, + "white": { + "value": "{color.neutral.0}", + "type": "color" + }, + "alpha-dark": { + "value": "{color.alpha.black.black 50%}", + "type": "color" + }, + "primary-lightest": { + "value": "{color.primary.25}", + "type": "color" + }, + "gray-darker": { + "value": "{color.neutral.400}", + "type": "color" + }, + "disabled": { + "value": "{color.neutral.100}", + "type": "color" + }, + "accent-light": { + "value": "{color.accent.300}", + "type": "color" + }, + "letter": { + "value": "{color.primary.200}", + "type": "color" + } + }, + "border": { + "primary": { + "value": "{color.primary.400}", + "type": "color" + }, + "primary-light": { + "value": "{color.primary.200}", + "type": "color" + }, + "error": { + "value": "{color.accent.500}", + "type": "color" + }, + "gray": { + "value": "{color.neutral.300}", + "type": "color" + }, + "gray-light": { + "value": "{color.neutral.200}", + "type": "color" + }, + "gray-dark": { + "value": "{color.neutral.600}", + "type": "color" + }, + "gray-darker": { + "value": "{color.neutral.800}", + "type": "color" + }, + "primary-lighter": { + "value": "{color.primary.100}", + "type": "color" + }, + "gray-lighter": { + "value": "{color.neutral.100}", + "type": "color" + } + }, + "divider": { + "gray": { + "value": "{color.neutral.100}", + "type": "color" + }, + "gray-dark": { + "value": "{color.neutral.600}", + "type": "color" + }, + "gray-light": { + "value": "{color.neutral.25}", + "type": "color" + }, + "inverse": { + "value": "{color.neutral.0}", + "type": "color" + } + }, + "action": { + "secondary-pressed": { + "value": "{color.neutral.100}", + "type": "color" + }, + "secondary": { + "value": "{color.neutral.0}", + "type": "color" + }, + "primary-pressed": { + "value": "{color.primary.400}", + "type": "color" + }, + "primary": { + "value": "{color.primary.25}", + "type": "color" + } + } + }, + "size-height": { + "1": { + "value": "{number.5}", + "type": "number" + }, + "2": { + "value": "{number.8}", + "type": "number" + }, + "3": { + "value": "{number.9}", + "type": "number" + }, + "4": { + "value": "{number.10}", + "type": "number" + }, + "5": { + "value": "{number.12}", + "type": "number" + }, + "6": { + "value": "{number.14}", + "type": "number" + }, + "7": { + "value": "{number.16}", + "type": "number" + }, + "8": { + "value": "{number.17}", + "type": "number" + }, + "9": { + "value": "{number.18}", + "type": "number" + }, + "10": { + "value": "{number.19}", + "type": "number" + }, + "11": { + "value": "{number.20}", + "type": "number" + } + }, + "gap": { + "g1": { + "value": "{number.2}", + "type": "number" + }, + "g2": { + "value": "{number.3}", + "type": "number" + }, + "g3": { + "value": "{number.5}", + "type": "number" + }, + "g4": { + "value": "{number.7}", + "type": "number" + }, + "g5": { + "value": "{number.8}", + "type": "number" + }, + "g6": { + "value": "{number.9}", + "type": "number" + }, + "g7": { + "value": "{number.10}", + "type": "number" + }, + "g8": { + "value": "{number.12}", + "type": "number" + }, + "g9": { + "value": "{number.14}", + "type": "number" + }, + "g10": { + "value": "{number.16}", + "type": "number" + }, + "g11": { + "value": "{number.18}", + "type": "number" + }, + "g12": { + "value": "{number.20}", + "type": "number" + } + }, + "padding": { + "p1": { + "value": "{number.2}", + "type": "number" + }, + "p2": { + "value": "{number.3}", + "type": "number" + }, + "p3": { + "value": "{number.5}", + "type": "number" + }, + "p4": { + "value": "{number.6}", + "type": "number" + }, + "p5": { + "value": "{number.7}", + "type": "number" + }, + "p6": { + "value": "{number.8}", + "type": "number" + }, + "p7": { + "value": "{number.9}", + "type": "number" + }, + "p8": { + "value": "{number.10}", + "type": "number" + }, + "p9": { + "value": "{number.12}", + "type": "number" + }, + "p10": { + "value": "{number.14}", + "type": "number" + } + }, + "radius": { + "xsmall": { + "value": "{number.2}", + "type": "number" + }, + "small": { + "value": "{number.3}", + "type": "number" + }, + "medium": { + "value": "{number.4}", + "type": "number" + }, + "large": { + "value": "{number.5}", + "type": "number" + }, + "xlarge": { + "value": "{number.6}", + "type": "number" + }, + "2xlarge": { + "value": "{number.7}", + "type": "number" + }, + "max": { + "value": "{number.max}", + "type": "number" + }, + "3xlarge": { + "value": "{number.9}", + "type": "number" + } + } + }, + "$themes": [], + "$metadata": { + "tokenSetOrder": ["global", "primitive/Mode 1", "semantic/Mode 1"] + } +} diff --git a/src/components/ui/EmojiLoading.tsx b/src/components/ui/EmojiLoading.tsx new file mode 100644 index 00000000..aa8aa26c --- /dev/null +++ b/src/components/ui/EmojiLoading.tsx @@ -0,0 +1,99 @@ +'use client'; +import { EP } from '@/global/api/ep'; +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; +import { useUploadingStore } from '@/store/useUploadingStore'; +import { motion } from 'framer-motion'; +import { useEffect, useState } from 'react'; + +import Toast from '@/global/components/toast/Toast'; +import BubbleTooltip from '@/global/components/tooltip/BubbleTooltip'; +import { useQueryClient } from '@tanstack/react-query'; + +interface EmojiLoadingProps { + duration?: number; + emoji?: string; + albumId?: string; +} +export default function EmojiLoading({ + duration = 3000, + emoji = 'U+1F60A', + albumId, +}: EmojiLoadingProps) { + const queryClient = useQueryClient(); + const [percent, setPercent] = useState(0); + + const displayEmoji = convertUnicodeToEmoji(emoji); + + useEffect(() => { + const timeout = setTimeout(() => { + let frame: number; + const startTime = performance.now(); + + const animate = (currentTime: number) => { + const elapsed = currentTime - startTime; + const progress = Math.min(elapsed / duration, 1); + setPercent(progress * 100); + + if (progress < 1) { + frame = requestAnimationFrame(animate); + } else { + // albumId를 props로 받아 invalidate + if (albumId) { + queryClient.invalidateQueries({ + queryKey: [EP.album.photos(albumId)], + }); + queryClient.invalidateQueries({ + queryKey: [EP.album.availableCount(albumId)], + }); + } + + const uploadedCount = useUploadingStore.getState().uploadedCount; + useUploadingStore.getState().reset(); + if (uploadedCount > 0) { + Toast.check(`총 ${uploadedCount}장을 앨범에 채웠어요.`); + } + } + }; + frame = requestAnimationFrame(animate); + return () => cancelAnimationFrame(frame); + }, 100); + + return () => clearTimeout(timeout); + }, [duration]); + + return ( +
+
+ + 0 + ? `conic-gradient(#FFCD14 0% ${percent}%, #FFE480 ${percent}%, white ${percent}% 100%)` + : 'white', + }} + /> +
+
+ {displayEmoji} +
+
+
+
+ ); +} diff --git a/src/components/ui/alert-dialog.tsx b/src/components/ui/alert-dialog.tsx new file mode 100644 index 00000000..6ceec312 --- /dev/null +++ b/src/components/ui/alert-dialog.tsx @@ -0,0 +1,157 @@ +'use client'; + +import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; +import * as React from 'react'; + +import { buttonVariants } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; + +function AlertDialog({ + ...props +}: React.ComponentProps) { + return ; +} + +function AlertDialogTrigger({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogPortal({ + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogContent({ + className, + ...props +}: React.ComponentProps) { + return ( + + + + + ); +} + +function AlertDialogHeader({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ); +} + +function AlertDialogFooter({ + className, + ...props +}: React.ComponentProps<'div'>) { + return ( +
+ ); +} + +function AlertDialogTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogAction({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function AlertDialogCancel({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogOverlay, + AlertDialogPortal, + AlertDialogTitle, + AlertDialogTrigger, +}; diff --git a/src/components/ui/button.tsx b/src/components/ui/button.tsx new file mode 100644 index 00000000..2f79011b --- /dev/null +++ b/src/components/ui/button.tsx @@ -0,0 +1,60 @@ +import { Slot } from '@radix-ui/react-slot'; +import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive", + { + variants: { + variant: { + default: 'bg-primary text-primary-foreground hover:bg-primary/90', + destructive: + 'bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60', + outline: + 'border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50', + secondary: + 'bg-secondary text-secondary-foreground hover:bg-secondary/80', + ghost: + 'hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50', + link: 'text-primary underline-offset-4 hover:underline', + }, + size: { + default: 'h-9 px-4 py-2 has-[>svg]:px-3', + sm: 'h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5', + lg: 'h-10 rounded-md px-6 has-[>svg]:px-4', + icon: 'size-9', + 'icon-sm': 'size-8', + 'icon-lg': 'size-10', + }, + }, + defaultVariants: { + variant: 'default', + size: 'default', + }, + }, +); + +function Button({ + className, + variant, + size, + asChild = false, + ...props +}: React.ComponentProps<'button'> & + VariantProps & { + asChild?: boolean; + }) { + const Comp = asChild ? Slot : 'button'; + + return ( + + ); +} + +export { Button, buttonVariants }; diff --git a/src/components/ui/calendar.tsx b/src/components/ui/calendar.tsx new file mode 100644 index 00000000..25042200 --- /dev/null +++ b/src/components/ui/calendar.tsx @@ -0,0 +1,222 @@ +'use client'; + +import { + ChevronDownIcon, + ChevronLeftIcon, + ChevronRightIcon, +} from 'lucide-react'; +import * as React from 'react'; +import { DayButton, DayPicker, getDefaultClassNames } from 'react-day-picker'; + +import { Button, buttonVariants } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; + +function Calendar({ + className, + classNames, + showOutsideDays = true, + captionLayout = 'label', + buttonVariant = 'ghost', + formatters, + fromDate, + toDate, + components, + ...props +}: React.ComponentProps & { + buttonVariant?: React.ComponentProps['variant']; + fromDate?: Date; + toDate?: Date; +}) { + const defaultClassNames = getDefaultClassNames(); + + return ( + svg]:rotate-180`, + String.raw`rtl:**:[.rdp-button\_previous>svg]:rotate-180`, + className, + )} + captionLayout={captionLayout} + formatters={{ + formatMonthDropdown: (date) => + date.toLocaleString('default', { month: 'short' }), + ...formatters, + }} + classNames={{ + root: cn('w-fit', defaultClassNames.root), + months: cn( + 'flex gap-4 flex-col md:flex-row relative', + defaultClassNames.months, + ), + month: cn('flex flex-col w-full gap-4', defaultClassNames.month), + nav: cn( + 'flex items-center gap-1 w-full absolute top-0 inset-x-0 justify-between', + defaultClassNames.nav, + ), + button_previous: cn( + buttonVariants({ variant: buttonVariant }), + 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none', + defaultClassNames.button_previous, + ), + button_next: cn( + buttonVariants({ variant: buttonVariant }), + 'size-(--cell-size) aria-disabled:opacity-50 p-0 select-none', + defaultClassNames.button_next, + ), + month_caption: cn( + 'flex items-center justify-center h-(--cell-size) w-full px-(--cell-size)', + defaultClassNames.month_caption, + ), + dropdowns: cn( + 'w-full flex items-center text-sm font-medium justify-center h-(--cell-size) gap-1.5', + defaultClassNames.dropdowns, + ), + dropdown_root: cn( + 'relative has-focus:border-ring border border-input shadow-xs has-focus:ring-ring/50 has-focus:ring-[3px] rounded-md', + defaultClassNames.dropdown_root, + ), + dropdown: cn( + 'absolute bg-popover inset-0 opacity-0', + defaultClassNames.dropdown, + ), + caption_label: cn( + 'select-none font-medium', + captionLayout === 'label' + ? 'text-sm' + : 'rounded-md pl-2 pr-1 flex items-center gap-1 text-sm h-8 [&>svg]:text-muted-foreground [&>svg]:size-3.5', + defaultClassNames.caption_label, + ), + table: 'w-full border-collapse', + weekdays: cn('flex', defaultClassNames.weekdays), + weekday: cn( + 'text-muted-foreground rounded-md flex-1 font-normal text-[0.8rem] select-none', + defaultClassNames.weekday, + ), + week: cn('flex w-full mt-2', defaultClassNames.week), + week_number_header: cn( + 'select-none w-(--cell-size)', + defaultClassNames.week_number_header, + ), + week_number: cn( + 'text-[0.8rem] select-none text-muted-foreground', + defaultClassNames.week_number, + ), + day: cn( + 'relative w-full h-full p-0 text-center [&:last-child[data-selected=true]_button]:rounded-r-md group/day aspect-square select-none', + props.showWeekNumber + ? '[&:nth-child(2)[data-selected=true]_button]:rounded-l-md' + : '[&:first-child[data-selected=true]_button]:rounded-l-md', + defaultClassNames.day, + ), + range_start: cn( + 'rounded-l-md bg-accent', + defaultClassNames.range_start, + ), + range_middle: cn('rounded-none', defaultClassNames.range_middle), + range_end: cn('rounded-r-md bg-accent', defaultClassNames.range_end), + today: cn( + 'bg-accent text-accent-foreground rounded-md data-[selected=true]:rounded-none', + defaultClassNames.today, + ), + outside: cn( + 'text-muted-foreground aria-selected:text-muted-foreground', + defaultClassNames.outside, + ), + disabled: cn( + 'text-muted-foreground opacity-50', + defaultClassNames.disabled, + ), + hidden: cn('invisible', defaultClassNames.hidden), + ...classNames, + }} + components={{ + Root: ({ className, rootRef, ...props }) => { + return ( +
+ ); + }, + Chevron: ({ className, orientation, ...props }) => { + if (orientation === 'left') { + return ( + + ); + } + + if (orientation === 'right') { + return ( + + ); + } + + return ( + + ); + }, + DayButton: CalendarDayButton, + WeekNumber: ({ children, ...props }) => { + return ( + +
+ {children} +
+ + ); + }, + ...components, + }} + {...props} + /> + ); +} + +function CalendarDayButton({ + className, + day, + modifiers, + ...props +}: React.ComponentProps) { + const defaultClassNames = getDefaultClassNames(); + + const ref = React.useRef(null); + React.useEffect(() => { + if (modifiers.focused) ref.current?.focus(); + }, [modifiers.focused]); + + return ( + + ); +} + +function CarouselNext({ + className, + variant = 'outline', + size = 'icon', + ...props +}: React.ComponentProps) { + const { orientation, scrollNext, canScrollNext } = useCarousel(); + + return ( + + ); +} + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +}; diff --git a/src/components/ui/drawer.tsx b/src/components/ui/drawer.tsx new file mode 100644 index 00000000..37f97bf7 --- /dev/null +++ b/src/components/ui/drawer.tsx @@ -0,0 +1,162 @@ +'use client'; + +import { useEffect } from 'react'; +import { Drawer as DrawerPrimitive } from 'vaul'; + +import { cn } from '@/lib/utils'; +function Drawer({ + ...props +}: React.ComponentProps) { + useEffect(() => { + const cleanup = () => { + document.body.removeAttribute('data-scroll-locked'); + document.body.style.paddingLeft = ''; + document.body.style.paddingRight = ''; + document.body.style.marginLeft = 'auto'; + document.body.style.marginRight = 'auto'; + document.body.style.overflow = ''; + }; + + const observer = new MutationObserver(() => { + if (document.body.hasAttribute('data-scroll-locked')) { + cleanup(); + } + }); + + observer.observe(document.body, { attributes: true }); + + return () => { + observer.disconnect(); + cleanup(); + }; + }, []); + return ; +} + +function DrawerTrigger({ + ...props +}: React.ComponentProps) { + return ; +} + +function DrawerPortal({ + ...props +}: React.ComponentProps) { + return ; +} + +function DrawerClose({ + ...props +}: React.ComponentProps) { + return ; +} + +function DrawerOverlay({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DrawerContent({ + className, + children, + showHandle = true, + ...props +}: React.ComponentProps & { + showHandle?: boolean; +}) { + return ( + + + + {showHandle && ( +
+ )} + {children} + + + ); +} + +function DrawerHeader({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); +} + +function DrawerFooter({ className, ...props }: React.ComponentProps<'div'>) { + return ( +
+ ); +} + +function DrawerTitle({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +function DrawerDescription({ + className, + ...props +}: React.ComponentProps) { + return ( + + ); +} + +export { + Drawer, + DrawerClose, + DrawerContent, + DrawerDescription, + DrawerFooter, + DrawerHeader, + DrawerOverlay, + DrawerPortal, + DrawerTitle, + DrawerTrigger, +}; diff --git a/src/components/ui/popover.tsx b/src/components/ui/popover.tsx new file mode 100644 index 00000000..841c81d1 --- /dev/null +++ b/src/components/ui/popover.tsx @@ -0,0 +1,34 @@ +// shadcn/ui Popover 컴포넌트 +'use client'; + +import * as RadixPopover from '@radix-ui/react-popover'; +import * as React from 'react'; + +import { cn } from '@/lib/utils'; + +const Popover = RadixPopover.Root; + +const PopoverTrigger = RadixPopover.Trigger; + +const PopoverAnchor = RadixPopover.Anchor; + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = 'center', sideOffset = 4, ...props }, ref) => ( + + + +)); +PopoverContent.displayName = RadixPopover.Content.displayName; + +export { Popover, PopoverAnchor, PopoverContent, PopoverTrigger }; diff --git a/src/feature/album-entry/components/FullSizeLetter.tsx b/src/feature/album-entry/components/FullSizeLetter.tsx new file mode 100644 index 00000000..0b683db3 --- /dev/null +++ b/src/feature/album-entry/components/FullSizeLetter.tsx @@ -0,0 +1,132 @@ +interface FullSizeLetterProps { + children: React.ReactNode; +} + +export default function FullSizeLetter({ children }: FullSizeLetterProps) { + return ( + <> + {/* 편지지 */} +
+ {children} +
+ + {/* 뒷편지봉투 svg */} + + + + + {/* 앞편지봉투 svg */} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} diff --git a/src/feature/album-entry/components/LetterContent.tsx b/src/feature/album-entry/components/LetterContent.tsx new file mode 100644 index 00000000..33a95889 --- /dev/null +++ b/src/feature/album-entry/components/LetterContent.tsx @@ -0,0 +1,80 @@ +'use client'; +import { useGetAlbumInvitation } from '@/feature/album/detail/hooks/useGetAlbumInvitation'; +import Toast from '@/global/components/toast/Toast'; +import { useCheckAuth } from '@/global/hooks/useCheckAuth'; +import { buildQuery } from '@/global/utils/buildQuery'; +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; +import { formatExpirationTime } from '@/global/utils/time/formatExpirationTime'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; + +interface LetterContentProps { + albumId: string; +} + +export default function LetterContent({ albumId }: LetterContentProps) { + const router = useRouter(); + const { data, isPending, isError } = useGetAlbumInvitation(albumId); + const { isAuthed } = useCheckAuth(); + + if (isPending) return null; + if (isError) return null; + if (!data) return null; + + const handleInviteAccept = async () => { + try { + if (isAuthed) { + router.push(`/photo/entry/${albumId}${buildQuery({ isInvite: true })}`); + } else { + router.push( + `/login${buildQuery({ redirect: encodeURIComponent(`/photo/entry/${albumId}${buildQuery({ isInvite: true })}`) })}`, + ); + } + } catch (error) { + Toast.alert('앨범 입장에 실패하였습니다'); + } + }; + + return ( + <> +
+ {data.makerName} + + {data.makerName} + +
+
+
+ {convertUnicodeToEmoji(data.themeEmoji)} +
+ +

+ {data.title} +

+ +

+ {data.eventDate} +

+ {!data.isExpired && ( + + 앨범 소멸까지 {formatExpirationTime(data.expiredAt)} + + )} + + +
+ + ); +} diff --git a/src/feature/album-entry/components/ScreenAlbumEntry.tsx b/src/feature/album-entry/components/ScreenAlbumEntry.tsx new file mode 100644 index 00000000..f85746af --- /dev/null +++ b/src/feature/album-entry/components/ScreenAlbumEntry.tsx @@ -0,0 +1,24 @@ +import LogoHeader from '@/global/components/header/LogoHeader'; +import FullSizeLetter from './FullSizeLetter'; +import LetterContent from './LetterContent'; + +interface ScreenAlbumEntryProps { + albumId: string; +} + +export default function ScreenAlbumEntry({ albumId }: ScreenAlbumEntryProps) { + return ( +
+ + +
+ + + + +
+ ); +} diff --git a/src/feature/album-entry/hooks/useAlbumEnterMutation.ts b/src/feature/album-entry/hooks/useAlbumEnterMutation.ts new file mode 100644 index 00000000..7770ee90 --- /dev/null +++ b/src/feature/album-entry/hooks/useAlbumEnterMutation.ts @@ -0,0 +1,25 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +const fetchData = async (albumId: string, redirectUrlOnAuthError?: string) => { + const res = await api.post({ + path: EP.album.enter(albumId), + ...(redirectUrlOnAuthError && { redirectUrlOnAuthError }), + }); + return res.result; +}; + +interface AlbumEnterProps { + albumId: string; + redirectUrlOnAuthError?: string; +} + +export function useAlbumEnterMutation() { + const mutation = useMutation({ + mutationFn: ({ albumId, redirectUrlOnAuthError }: AlbumEnterProps) => + fetchData(albumId, redirectUrlOnAuthError), + }); + + return mutation; +} diff --git a/src/feature/album-select/components/SelectAlbumBody.tsx b/src/feature/album-select/components/SelectAlbumBody.tsx new file mode 100644 index 00000000..74cea9dd --- /dev/null +++ b/src/feature/album-select/components/SelectAlbumBody.tsx @@ -0,0 +1,233 @@ +'use client'; +import { useCheckImages } from '@/feature/create-album/hook/useCheckImages'; +import { getFilesWithCaptureTime } from '@/feature/create-album/utils/getFilesWithCaptureTime'; +import { validateImages } from '@/feature/create-album/utils/validateImages'; +import LongButton from '@/global/components/LongButton'; +import PhotoBox from '@/global/components/photo/PhotoBox'; +import Toast from '@/global/components/toast/Toast'; +import { usePresignedAndUploadToNCP } from '@/global/hooks/usePresignedAndUploadToNCP'; +import { useReportFailed } from '@/global/hooks/useReportFailed'; +import { useImageStore } from '@/store/useImageStore'; +import { useUploadingStore } from '@/store/useUploadingStore'; +import { useParams, useRouter } from 'next/navigation'; +import { useEffect, useMemo, useState } from 'react'; + +type ImageWithUrl = { + id: string; + file: File; + url: string; + isOversized: boolean; +}; + +export default function SelectAlbumBody() { + const isUploaded = useUploadingStore((state) => state.isUploaded); + const handleUpload = async () => { + const selectedFiles = processedImages.filter((img) => + selectedIds.has(img.id), + ); + const files = selectedFiles.map((img) => img.file); + const filesWithCapture = await getFilesWithCaptureTime(files); + const fileInfos = filesWithCapture.map(({ file, captureTime }) => ({ + fileName: file.name, + fileSize: file.size, + contentType: file.type, + captureTime, + })); + + uploadMutate({ + albumCode: albumId, + fileInfos, + files, + }); + }; + const { albumId } = useParams() as { albumId: string }; + const { images } = useImageStore(); + const router = useRouter(); + + useEffect(() => { + if (images.length === 0 && albumId) { + router.push(`/album/upload/${albumId}`); + } + }, [images, albumId, router]); + const [selectedIds, setSelectedIds] = useState>(new Set()); + const [availableCount, setAvailableCount] = useState(null); + const { mutate: checkImagesMutate } = useCheckImages(); + + const revokeAllObjectUrls = () => { + processedImages.forEach((img) => { + URL.revokeObjectURL(img.url); + }); + }; + + const { mutate: reportFailed } = useReportFailed(); + + const { mutate: uploadMutate } = usePresignedAndUploadToNCP({ + onSuccess: (result) => { + if (result.failed > 0) { + Toast.alert(`${result.failed}개 파일 업로드에 실패했어요`); + if ( + Array.isArray(result.failedPhotoIds) && + result.failedPhotoIds.length > 0 + ) { + reportFailed(result.failedPhotoIds); + } + } else { + revokeAllObjectUrls(); + useUploadingStore.getState().setUploaded(true); + useUploadingStore.getState().setUploadedCount(result.success); + router.replace(`/album/detail/${albumId}`); + } + }, + onError: (e) => { + revokeAllObjectUrls(); + console.error('에러 발생', e); + alert('사진을 업로드하는 중 오류가 발생했습니다. 다시 시도해주세요.'); + }, + }); + + const processedImages = useMemo(() => { + const validation = validateImages(images.map((img) => img.file)); + const oversizedSet = new Set(validation.oversizedFiles); + + return images.map((img) => ({ + id: img.id, + file: img.file, + url: URL.createObjectURL(img.file), + isOversized: oversizedSet.has(img.file.name), + })); + }, [images]); + + // 뒤로가기(나가기) 또는 페이지 이탈 시 object URL 해제 + useEffect(() => { + const handleRevoke = () => { + revokeAllObjectUrls(); + }; + window.addEventListener('pagehide', handleRevoke); + window.addEventListener('popstate', handleRevoke); + return () => { + window.removeEventListener('pagehide', handleRevoke); + window.removeEventListener('popstate', handleRevoke); + revokeAllObjectUrls(); + }; + }, [processedImages]); + + const validImages = useMemo( + () => processedImages.filter((img) => !img.isOversized), + [processedImages], + ); + + useEffect(() => { + if (!validImages.length) { + setSelectedIds(new Set()); + return; + } + if (!availableCount || validImages.length <= availableCount) { + setSelectedIds(new Set(validImages.map((img) => img.id))); + } else { + setSelectedIds( + new Set(validImages.slice(0, availableCount).map((img) => img.id)), + ); + } + }, [validImages, availableCount]); + + useEffect(() => { + const files = images.map((img) => img.file); + if (!files.length || !albumId) return; + + checkImagesMutate( + { files, albumId }, + { + onSuccess: ({ oversizedFiles, availableCount }) => { + setAvailableCount(availableCount); + const msgs: string[] = []; + if (oversizedFiles.length > 0) { + msgs.push( + `6MB를 초과한 사진 ${oversizedFiles.length}장이 제외되었어요.`, + ); + } + if (files.length > availableCount) { + msgs.push('지금 앨범에 담을 수 있는 만큼만 선택되었어요.'); + } + if (msgs.length) Toast.alert(msgs.join('\n')); + }, + onError: () => { + Toast.alert('이미지 검증 중 오류가 발생했어요.'); + }, + }, + ); + }, [images, albumId, checkImagesMutate]); + + const toggleSelect = ( + id: string, + isOversized: boolean, + nextSelected?: boolean, + ) => { + if (isOversized) return; // 6MB 초과는 선택 불가 + + setSelectedIds((prev) => { + const updated = new Set(prev); + if (typeof nextSelected === 'boolean') { + if (nextSelected) updated.add(id); + else updated.delete(id); + return updated; + } + if (updated.has(id)) updated.delete(id); + else updated.add(id); + return updated; + }); + }; + + const isOverCount = + availableCount !== null && selectedIds.size > availableCount; + + return ( +
+
+ + 총 {images.length}장 + + + {selectedIds.size}/{availableCount} + +
+
+ {processedImages.map((img) => { + const isSelected = selectedIds.has(img.id); + return ( +
+ { + toggleSelect(img.id, img.isOversized, next); + }} + onDisabledPress={() => { + Toast.alert('사진이 6MB를 초과해 업로드할 수 없어요.'); + }} + /> +
+ ); + })} +
+ +
+ ); +} diff --git a/src/feature/album/4cut/components/Capture4CutPortal.tsx b/src/feature/album/4cut/components/Capture4CutPortal.tsx new file mode 100644 index 00000000..8deff1da --- /dev/null +++ b/src/feature/album/4cut/components/Capture4CutPortal.tsx @@ -0,0 +1,42 @@ +'use client'; + +import { BodyPortal } from '@/global/components/portal/BodyPortal'; +import type { RefObject } from 'react'; +import Container4Cut from './Container4Cut'; + +interface Capture4CutPortalProps { + captureRef: RefObject; + visible: boolean; + albumId: string; + eventName?: string; + eventDate?: string; +} + +const Capture4CutPortal = ({ + captureRef, + visible, + albumId, + eventName, + eventDate, +}: Capture4CutPortalProps) => ( + +
+ +
+
+); + +export default Capture4CutPortal; diff --git a/src/feature/album/4cut/components/Container4Cut.tsx b/src/feature/album/4cut/components/Container4Cut.tsx new file mode 100644 index 00000000..b3a71ca3 --- /dev/null +++ b/src/feature/album/4cut/components/Container4Cut.tsx @@ -0,0 +1,109 @@ +import { useBase64Images } from '@/global/hooks/useBase64Images'; +import { useMemo } from 'react'; +import { use4CutPreviewQuery } from '../hooks/use4CutPreviewQuery'; +import Svg4Cut from '../svg/Svg4Cut'; + +interface Container4CutProps { + albumId: string; + eventName?: string; + eventDate?: string; + scale?: number; + width?: number; +} + +const BASE_WIDTH = 216; +const BASE_HEIGHT = 384; +const BASE_ASPECT_RATIO = BASE_HEIGHT / BASE_WIDTH; +const BASE_FONT_SIZE = 7.963; +const BASE_NAME_POSITION = { + bottom: 7.4, + left: 9.6, +}; +const BASE_DATE_POSITION = { + bottom: 7.4, + right: 10.4, +}; + +export default function Container4Cut({ + albumId, + eventDate, + eventName, + scale = 1, + width, +}: Container4CutProps) { + // TODO : openapi type이 이상해서 임시 any처리. 백엔드랑 협의 필요 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const { data }: any = use4CutPreviewQuery(albumId); + + const images = useMemo(() => { + return ( + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data?.previewPhotos?.map((item: any) => item.imageUrl) ?? + // eslint-disable-next-line @typescript-eslint/no-explicit-any + data?.photos?.map((item: any) => item.imageUrl) ?? + [] + ); + }, [data]); + + const { base64List } = useBase64Images({ imageUrls: images }); + + const calculatedWidth = width ?? BASE_WIDTH * scale; + const calculatedHeight = calculatedWidth * BASE_ASPECT_RATIO; + const calculatedScale = calculatedWidth / BASE_WIDTH; + + const scaledFontSize = BASE_FONT_SIZE * calculatedScale; + const scaledNamePosition = { + bottom: `${BASE_NAME_POSITION.bottom * calculatedScale}px`, + left: `${BASE_NAME_POSITION.left * calculatedScale}px`, + }; + const scaledDatePosition = { + bottom: `${BASE_DATE_POSITION.bottom * calculatedScale}px`, + right: `${BASE_DATE_POSITION.right * calculatedScale}px`, + }; + + return ( +
+ + + {eventName && ( + + {eventName} + + )} + + {eventDate && ( + + {eventDate} + + )} + + {/* 이 컴포넌트에서만 사용되는 폰트 */} + +
+ ); +} diff --git a/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx b/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx new file mode 100644 index 00000000..6fafc0b7 --- /dev/null +++ b/src/feature/album/4cut/components/ScreenAlbum4Cut.tsx @@ -0,0 +1,276 @@ +'use client'; +import { useGetUserMe } from '@/feature/main/hooks/useGetUserMe'; +import { EP } from '@/global/api/ep'; +import CustomHeader from '@/global/components/header/CustomHeader'; +import LongButton from '@/global/components/LongButton'; +import ConfirmModal from '@/global/components/modal/ConfirmModal'; +import Toast from '@/global/components/toast/Toast'; +import BubbleTooltip from '@/global/components/tooltip/BubbleTooltip'; +import PersonSvg from '@/global/svg/PersonSvg'; +import { downloadFile } from '@/global/utils/downloadFile'; +import { getDeviceType } from '@/global/utils/getDeviceType'; +import { extractHtmlToBlob } from '@/global/utils/image/extractHtmlToBlob'; +import { shareImage } from '@/global/utils/image/shareImage'; +import { shareViaNavigator } from '@/global/utils/shareNavigator'; +import { useQueryClient } from '@tanstack/react-query'; +import { Download, Loader2, LucideIcon, Menu, Send } from 'lucide-react'; +import dynamic from 'next/dynamic'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useRef, useState } from 'react'; +import { useGetAlbumInfo } from '../../detail/hooks/useGetAlbumInfo'; +import { use4CutFixed } from '../hooks/use4CutFixed'; +import { use4CutPreviewQuery } from '../hooks/use4CutPreviewQuery'; +import Container4Cut from './Container4Cut'; +const Capture4CutPortal = dynamic(() => import('./Capture4CutPortal'), { + ssr: false, +}); + +interface ScreenAlbum4CutProps { + albumId: string; +} + +export default function ScreenAlbum4Cut({ albumId }: ScreenAlbum4CutProps) { + const router = useRouter(); + const queryClient = useQueryClient(); + const [isConfirmed, setIsConfirmed] = useState(false); + const [isCaptureVisible, setIsCaptureVisible] = useState(false); + const [isDownloading, setIsDownloading] = useState(false); + const captureRef = useRef(null); + const { data } = useGetAlbumInfo(albumId); + const { data: { name } = {} } = useGetUserMe(); + + // TODO : openapi type이 이상해서 임시 any처리. 백엔드랑 협의 필요 + + const { + data: { myRole, previewPhotos, isFinalized } = {}, + isPending: is4CutPreviewPending, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + }: any = use4CutPreviewQuery(albumId); + const { mutateAsync } = use4CutFixed(); + + const isMaker = myRole === 'MAKER'; + + const showCaptureNode = async () => + new Promise((resolve) => { + setIsCaptureVisible(true); + requestAnimationFrame(() => resolve()); + }); + + const handleConfirm = async (): Promise => { + await mutateAsync({ + albumId, + photoIds: previewPhotos.map( + (photo: { photoId: number; imageUrl: string; photoRank: number }) => + photo.photoId, + ), + }); + queryClient.invalidateQueries({ + queryKey: [EP.cheese4cut.preview(albumId)], + }); + setIsConfirmed(true); + }; + + const handleDownload = async () => { + const deviceType = getDeviceType(); + + if (!captureRef.current) { + Toast.alert( + '다운로드할 이미지를 찾지 못했어요. 잠시 후 다시 시도해주세요.', + ); + return; + } + + try { + setIsDownloading(true); + await showCaptureNode(); + + const fileName = data?.title + ? `${data.title}-cheese-4cut.png` + : 'cheese-4cut.png'; + const blob = await extractHtmlToBlob(captureRef.current); + + if (deviceType === 'ios') { + await shareImage({ + imageBlobs: blob, + imageTitle: fileName, + onError: () => { + downloadFile(blob, fileName); + }, + }); + } else { + downloadFile(blob, fileName); + } + } catch (error) { + console.error(error); + Toast.alert('이미지를 다운로드하지 못했습니다. 다시 시도해주세요.'); + } finally { + setIsCaptureVisible(false); + setIsDownloading(false); + } + }; + + const handleShare = async () => { + if (!captureRef.current) { + Toast.alert('공유할 이미지를 찾지 못했어요. 잠시 후 다시 시도해주세요.'); + return; + } + + try { + await showCaptureNode(); + + const fileName = data?.title + ? `${data.title}-cheese-4cut.png` + : 'cheese-4cut.png'; + const blob = await extractHtmlToBlob(captureRef.current); + + await shareImage({ + imageBlobs: blob, + imageTitle: fileName, + onError: () => { + downloadFile(blob, fileName); + }, + }); + } catch (error) { + console.error('Failed to share 4cut preview:', error); + Toast.alert('이미지를 생성하지 못했습니다. 다시 시도해주세요.'); + } finally { + setIsCaptureVisible(false); + } + }; + + return ( + <> + + + + +
+ } + /> +
+ {!isFinalized && ( +
현재 TOP 4 사진
+ )} +
+ +
+
+ + {!is4CutPreviewPending && ( +
+ {isMaker || isFinalized ? ( + <> + {isFinalized ? ( +
+ + +
+ ) : ( + <> +
+ 띱 진행상황 +
+ + + + + {`${data?.currentParticipant} / ${data?.participant}`}{' '} + 명 + +
+
+ } + title='이대로 확정하시겠어요?' + description='예쁜 치즈네컷을 만들어드릴게요' + confirmText='확정하기' + onConfirm={handleConfirm} + /> + + )} + + ) : ( +
+ + { + if (!data) return; + + await shareViaNavigator({ + data: { + title: `'${data.title}'앨범에 대한 치즈네컷을 선정해주세요`, + text: `${name}님이 메이커님에게 조르기를 요청했어요!`, + url: `https://say-cheese.me/album/4cut/${albumId}`, + }, + errorMessage: + '공유에 실패하였습니다. 다시한번 시도해주세요.', + }); + }} + /> +
+ )} +
+ )} + {isDownloading && ( +
+
+ + + 다운로드 중... + +
+
+ )} + + + ); +} + +interface ActionButtonProps { + icon: LucideIcon; + text: string; + onClick: () => void; +} + +export const ActionButton = ({ + icon: Icon, + text, + onClick, +}: ActionButtonProps) => ( + +); diff --git a/src/feature/album/4cut/hooks/use4CutFixed.ts b/src/feature/album/4cut/hooks/use4CutFixed.ts new file mode 100644 index 00000000..d5b6eeee --- /dev/null +++ b/src/feature/album/4cut/hooks/use4CutFixed.ts @@ -0,0 +1,25 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +interface Cheese4CutFixedProps { + albumId: string; + photoIds: number[]; +} + +const fetchData = async ({ albumId, photoIds }: Cheese4CutFixedProps) => { + const res = await api.post({ + path: EP.cheese4cut.finalize(albumId), + body: { photoIds }, + }); + return res.result; +}; + +export function use4CutFixed() { + const mutation = useMutation({ + mutationFn: ({ albumId, photoIds }: Cheese4CutFixedProps) => + fetchData({ albumId, photoIds }), + }); + + return mutation; +} diff --git a/src/feature/album/4cut/hooks/use4CutPreviewQuery.ts b/src/feature/album/4cut/hooks/use4CutPreviewQuery.ts new file mode 100644 index 00000000..00607614 --- /dev/null +++ b/src/feature/album/4cut/hooks/use4CutPreviewQuery.ts @@ -0,0 +1,24 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; + +const fetchData = async (albumId: string) => { + const response = await api.get({ + path: EP.cheese4cut.preview(albumId), + }); + + return response.result; +}; + +export function use4CutPreviewQuery( + albumId: string, + options?: UseQueryOptions, +) { + const query = useQuery({ + queryKey: [EP.cheese4cut.preview(albumId)], + queryFn: () => fetchData(albumId), + ...options, + }); + + return query; +} diff --git a/src/feature/album/4cut/svg/Svg4Cut.tsx b/src/feature/album/4cut/svg/Svg4Cut.tsx new file mode 100644 index 00000000..e2c83675 --- /dev/null +++ b/src/feature/album/4cut/svg/Svg4Cut.tsx @@ -0,0 +1,71 @@ +const PHOTO_SLOTS = [ + { x: 65, y: 78.5 }, + { x: 552, y: 78.5 }, + { x: 552, y: 788.5 }, + { x: 65, y: 788.5 }, +] as const; + +type PhotoUrl = string | null | undefined; + +interface Svg4CutProps { + width: number | `${number}`; + height: number | `${number}`; + photos?: ReadonlyArray; +} + +export default function Svg4Cut({ photos, height, width }: Svg4CutProps) { + const photoSources = photos ?? []; + + return ( + + {PHOTO_SLOTS.map((pos, i) => { + if (!photoSources[i]) return null; + + return ( + + ); + })} + + + + + + + + + + + ); +} diff --git a/src/feature/album/detail/api/getPhotoListByAlbumId.server.ts b/src/feature/album/detail/api/getPhotoListByAlbumId.server.ts new file mode 100644 index 00000000..725505f6 --- /dev/null +++ b/src/feature/album/detail/api/getPhotoListByAlbumId.server.ts @@ -0,0 +1,49 @@ +import { PhotoSorting } from '@/global/api/ep'; +import { serverApi } from '@/global/utils/serverApi'; + +export interface GetPhotoListParams { + page?: number; + size?: number; + sorting?: PhotoSorting; +} + +export interface Photo { + photoId: number; + thumbnailUrl: string; + likesCnt: number; + isLiked: boolean; + isDownloaded: boolean; + isRecentlyDownloaded: boolean; +} + +export interface PhotoListResult { + responses: Photo[]; + listSize: number; + isFirst: boolean; + isLast: boolean; + hasNext: boolean; +} + +/** + * 앨범의 사진 리스트를 가져오는 API (서버/클라이언트 공용) + * @param albumId 앨범 코드(string) + * @param params page, size, sorting 쿼리 파라미터 + * @returns 사진 리스트 + */ +export async function getPhotoListByAlbumId( + albumId: string, + params?: GetPhotoListParams, +): Promise { + if (!albumId) throw new Error('albumId가 필요합니다'); + const queryParams: Record = {}; + if (params) { + if (params.page !== undefined) queryParams.page = params.page; + if (params.size !== undefined) queryParams.size = params.size; + if (params.sorting !== undefined) queryParams.sorting = params.sorting; + } + const res = await serverApi.get({ + path: `/v1/album/${albumId}/photos`, + params: queryParams, + }); + return res.result as PhotoListResult; +} diff --git a/src/feature/album/detail/components/AlbumBestCut.tsx b/src/feature/album/detail/components/AlbumBestCut.tsx new file mode 100644 index 00000000..bcc55d49 --- /dev/null +++ b/src/feature/album/detail/components/AlbumBestCut.tsx @@ -0,0 +1,37 @@ +import LongButton from '@/global/components/LongButton'; +import { useRouter } from 'next/navigation'; +import AlbumBestCutPhotoList from './AlbumBestCutPhotoList'; + +interface AlbumBestCutProps { + albumId: string; + photoCount?: number; +} + +export default function AlbumBestCut({ + albumId, + photoCount, +}: AlbumBestCutProps) { + const router = useRouter(); + + if (photoCount === undefined || photoCount === 0) return null; + + return ( +
+

+ 앨범 베스트컷 +

+ +
+ +
+ + router.push(`/album/4cut/${albumId}`)} + noFixed + disabled={photoCount < 4} + height={48} + /> +
+ ); +} diff --git a/src/feature/album/detail/components/AlbumBestCutPhotoList.tsx b/src/feature/album/detail/components/AlbumBestCutPhotoList.tsx new file mode 100644 index 00000000..533c3614 --- /dev/null +++ b/src/feature/album/detail/components/AlbumBestCutPhotoList.tsx @@ -0,0 +1,46 @@ +import { useAlbumPhotosInfiniteQuery } from '@/feature/photo-detail/hooks/useAlbumPhotosInfiniteQuery'; +import PhotoBox from '@/global/components/photo/PhotoBox'; +import { buildQuery } from '@/global/utils/buildQuery'; +import { useRouter } from 'next/navigation'; + +interface AlbumBestCutPhotoListProps { + albumId: string; +} + +export default function AlbumBestCutPhotoList({ + albumId, +}: AlbumBestCutPhotoListProps) { + const router = useRouter(); + const { isPending, isError, items } = useAlbumPhotosInfiniteQuery({ + code: albumId, + size: 4, + sorting: 'POPULAR', + // 좋아요 누른것 실시간으로 반영되게 매번 호출 + refetchOnMount: 'always', + }); + + // Layout Shifting 방지 위해 height 고정 + if (isPending) return
; + if (isError) return
; + + return ( +
+ {items.map(({ thumbnailUrl, photoId, likeCnt, isLiked }) => { + if (!photoId) return null; + + return ( + + router.push(`/photo/detail/${albumId}${buildQuery({ photoId })}`) + } + imageSrc={thumbnailUrl} + likeCount={likeCnt} + liked={isLiked} + /> + ); + })} +
+ ); +} diff --git a/src/feature/album/detail/components/AlbumBottomActions.tsx b/src/feature/album/detail/components/AlbumBottomActions.tsx new file mode 100644 index 00000000..3d532ff1 --- /dev/null +++ b/src/feature/album/detail/components/AlbumBottomActions.tsx @@ -0,0 +1,43 @@ +import DownloadActionBar from './DownloadActionBar'; +import NavBarAlbumDetail from './NavBarAlbumDetail'; +import { AlbumDetailMode } from './ScreenAlbumDetail'; +import UploadButtonInDetail from './UploadButtonInDetail'; + +interface AlbumBottomActionsProps { + mode: AlbumDetailMode; + albumId: string; + changeAlbumMode: (newMode: AlbumDetailMode) => void; + selectedCount: number; + totalPhotoCount?: number; + isLoading: boolean; +} + +export default function AlbumBottomActions({ + mode, + albumId, + changeAlbumMode, + selectedCount, + totalPhotoCount, + isLoading, +}: AlbumBottomActionsProps) { + if (isLoading) return null; + + if (!totalPhotoCount) + return ; + + if (mode === 'default') { + return ; + } + + if (mode === 'select') { + return ( + + ); + } + + return null; +} diff --git a/src/feature/album/detail/components/AlbumInfoSummary.tsx b/src/feature/album/detail/components/AlbumInfoSummary.tsx new file mode 100644 index 00000000..59502b62 --- /dev/null +++ b/src/feature/album/detail/components/AlbumInfoSummary.tsx @@ -0,0 +1,42 @@ +import { AlbumInvitationResponseSchema } from '@/global/api/ep'; +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; + +// 😀 <- 이 이모지 +const DEFAULT_EMOJI = 'U+1F600'; + +interface AlbumInfoSummaryProps { + albumInfo?: AlbumInvitationResponseSchema; + isLoading: boolean; + isError: boolean; +} + +export function AlbumInfoSummary({ + albumInfo, + isLoading, + isError, +}: AlbumInfoSummaryProps) { + if (isLoading) { + return
; + } + if (isError || !albumInfo) { + return
; + } + + const emoji = convertUnicodeToEmoji(albumInfo.themeEmoji ?? DEFAULT_EMOJI); + + return ( +
+
+ {emoji} +
+
+

+ {albumInfo.title} +

+ + {albumInfo.eventDate} + +
+
+ ); +} diff --git a/src/feature/album/detail/components/AlbumInfos.tsx b/src/feature/album/detail/components/AlbumInfos.tsx new file mode 100644 index 00000000..5e7c6242 --- /dev/null +++ b/src/feature/album/detail/components/AlbumInfos.tsx @@ -0,0 +1,43 @@ +'use client'; + +import { AlbumInvitationResponseSchema } from '@/global/api/ep'; +import { useAlbumTypeStore } from '@/store/useAlbumTypeStore'; +import { forwardRef } from 'react'; +import { useShallow } from 'zustand/shallow'; +import AlbumBestCut from './AlbumBestCut'; +import { AlbumInfoSummary } from './AlbumInfoSummary'; + +interface AlbumInfosProps { + albumId: string; + albumInfo?: AlbumInvitationResponseSchema; + isLoading: boolean; + isError: boolean; + photoCount?: number; +} + +const AlbumInfos = forwardRef( + ({ albumId, photoCount, ...rest }, ref) => { + const { albumType, setAlbumType } = useAlbumTypeStore( + useShallow((state) => ({ + albumType: state.albumType, + setAlbumType: state.setAlbumType, + })), + ); + + if (albumType === 'deep') return null; + + return ( +
+ + +
+ ); + }, +); + +AlbumInfos.displayName = 'AlbumInfos'; + +export default AlbumInfos; diff --git a/src/feature/album/detail/components/AlbumPhotoSection.tsx b/src/feature/album/detail/components/AlbumPhotoSection.tsx new file mode 100644 index 00000000..20d1ee9f --- /dev/null +++ b/src/feature/album/detail/components/AlbumPhotoSection.tsx @@ -0,0 +1,81 @@ +import { PhotoListResponseSchema } from '@/global/api/ep'; +import Spinner from '@/global/components/Spinner'; +import { useAlbumTypeStore } from '@/store/useAlbumTypeStore'; +import { + FetchNextPageOptions, + InfiniteQueryObserverResult, +} from '@tanstack/react-query'; +import { useShallow } from 'zustand/shallow'; +import NoPhotoBody from './NoPhotoBody'; +import PhotoList from './PhotoList'; +import { AlbumDetailMode } from './ScreenAlbumDetail'; + +interface AlbumPhotoSectionProps { + isLoading: boolean; + photos: PhotoListResponseSchema[]; + selectionResetKey: number; + albumId: string; + mode: AlbumDetailMode; + onChangeMode: (mode: AlbumDetailMode) => void; + fetchNextPage: ( + options?: FetchNextPageOptions, + ) => Promise; + hasNextPage: boolean; + isFetchingNextPage: boolean; + totalPhotoCount?: number; +} + +export default function AlbumPhotoSection({ + isLoading, + photos, + selectionResetKey, + albumId, + mode, + onChangeMode, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + totalPhotoCount, +}: AlbumPhotoSectionProps) { + const { albumType } = useAlbumTypeStore( + useShallow((state) => ({ + albumType: state.albumType, + setAlbumType: state.setAlbumType, + })), + ); + + if (isLoading) + return ( +
+ +
+ ); + + if (photos.length === 0) { + return ( + + ); + } + + return ( + + ); +} diff --git a/src/feature/album/detail/components/DownloadActionBar.tsx b/src/feature/album/detail/components/DownloadActionBar.tsx new file mode 100644 index 00000000..c48aae5c --- /dev/null +++ b/src/feature/album/detail/components/DownloadActionBar.tsx @@ -0,0 +1,79 @@ +import { usePhotoDownloadMutation } from '@/feature/photo-detail/hooks/usePhotoDownloadMutation'; +import Toast from '@/global/components/toast/Toast'; +import { downloadFile } from '@/global/utils/downloadFile'; +import { getDeviceType } from '@/global/utils/getDeviceType'; +import { shareImage } from '@/global/utils/image/shareImage'; +import { useSelectedPhotosStore } from '@/store/useSelectedPhotosStore'; +import { useShallow } from 'zustand/shallow'; +import { AlbumDetailMode } from './ScreenAlbumDetail'; + +interface DownloadActionBarProps { + albumId: string; + selectedCount: number; + changeAlbumMode: (newMode: AlbumDetailMode) => void; +} + +export default function DownloadActionBar({ + albumId, + selectedCount, + changeAlbumMode, +}: DownloadActionBarProps) { + const { mutateAsync } = usePhotoDownloadMutation(); + const { selectedPhotos, clearSelectedPhotos } = useSelectedPhotosStore( + useShallow((state) => ({ + selectedPhotos: state.selectedPhotos, + clearSelectedPhotos: state.clearSelectedPhotos, + })), + ); + + const handleDownload = async () => { + if (selectedCount === 0) return; + + const photoUrls = selectedPhotos.map((photo) => photo.url); + const photoIds = selectedPhotos.map((photo) => photo.id); + try { + const deviceType = getDeviceType(); + + if (deviceType === 'ios') { + shareImage({ + imageUrls: photoUrls, + onSuccess: () => { + changeAlbumMode('default'); + clearSelectedPhotos(); + window.scrollTo({ top: 0, behavior: 'smooth' }); + mutateAsync({ albumId, photoIds }); + }, + onError: () => { + Toast.alert('사진을 준비하는 중 오류가 발생했습니다.'); + }, + }); + } else { + await downloadFile(photoUrls); + changeAlbumMode('default'); + clearSelectedPhotos(); + window.scrollTo({ top: 0, behavior: 'smooth' }); + mutateAsync({ albumId, photoIds }); + } + } catch (e) { + console.error('사진 다운로드 처리 중 오류 발생:', e); + } + }; + + const isDisabled = selectedCount === 0; + + return ( +
+
+ {selectedCount}장의 사진이 선택됨 +
+ +
+ ); +} diff --git a/src/feature/album/detail/components/NavBarAlbumDetail.tsx b/src/feature/album/detail/components/NavBarAlbumDetail.tsx new file mode 100644 index 00000000..2ead600e --- /dev/null +++ b/src/feature/album/detail/components/NavBarAlbumDetail.tsx @@ -0,0 +1,103 @@ +'use client'; +import { handleFileUpload } from '@/feature/create-album/utils/handleFileUpload'; +import ToggleAlbumType from '@/feature/main/components/open-album/ToggleAlbumType'; +import BottomSheetModal from '@/global/components/modal/BottomSheetModal'; +import Toast from '@/global/components/toast/Toast'; +import { useAlbumSortStore } from '@/store/useAlbumSortStore'; +import { useAlbumTypeStore } from '@/store/useAlbumTypeStore'; +import { ArrowDownUp, Plus } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { useRef } from 'react'; +import { useShallow } from 'zustand/shallow'; +import SelectPhotoSortType from './SelectPhotoSortType'; + +export type AlbumType = 'all' | 'deep'; + +interface NavBarAlbumDetailProps { + albumId: string; +} + +export default function NavBarAlbumDetail({ albumId }: NavBarAlbumDetailProps) { + const router = useRouter(); + const fileInputRef = useRef(null); + const { sortType, setSortType } = useAlbumSortStore( + useShallow((state) => ({ + sortType: state.sortType, + setSortType: state.setSortType, + })), + ); + const { albumType, setAlbumType } = useAlbumTypeStore( + useShallow((state) => ({ + albumType: state.albumType, + setAlbumType: state.setAlbumType, + })), + ); + + const handleToggleChange = (value: AlbumType): void => { + setAlbumType(value); + }; + + const handleButtonClick = (): void => { + fileInputRef.current?.click(); + }; + + const onFileChange = async ( + e: React.ChangeEvent, + ): Promise => { + try { + const result = await handleFileUpload(e, albumId, router, { + stay: true, + }); + const success = result?.success ?? 0; + } catch (error: unknown) { + console.error('error', typeof error); + } + }; + + return ( +
+ + + + } + > + setSortType(newType)} + /> + + + + + +
+ ); +} diff --git a/src/feature/album/detail/components/NoPhotoBody.tsx b/src/feature/album/detail/components/NoPhotoBody.tsx new file mode 100644 index 00000000..5b355905 --- /dev/null +++ b/src/feature/album/detail/components/NoPhotoBody.tsx @@ -0,0 +1,34 @@ +import { useQueryClient } from '@tanstack/react-query'; +import Image from 'next/image'; + +interface NoPhotoBodyProps { + text: string; + isRefresh?: boolean; +} + +export default function NoPhotoBody({ text, isRefresh }: NoPhotoBodyProps) { + const queryClient = useQueryClient(); + const handleRefresh = () => { + queryClient.invalidateQueries(); + }; + return ( +
+ 사진 없음 + {text} + {isRefresh && ( + + )} +
+ ); +} diff --git a/src/feature/album/detail/components/PhotoList.tsx b/src/feature/album/detail/components/PhotoList.tsx new file mode 100644 index 00000000..882abc22 --- /dev/null +++ b/src/feature/album/detail/components/PhotoList.tsx @@ -0,0 +1,230 @@ +'use client'; +import { PhotoListResponseSchema } from '@/global/api/ep'; +import PhotoBox from '@/global/components/photo/PhotoBox'; +import Toast from '@/global/components/toast/Toast'; +import { buildQuery } from '@/global/utils/buildQuery'; +import { useAlbumSortStore } from '@/store/useAlbumSortStore'; +import { useAlbumTypeStore } from '@/store/useAlbumTypeStore'; +import { useSelectedPhotosStore } from '@/store/useSelectedPhotosStore'; +import { + type FetchNextPageOptions, + type InfiniteQueryObserverResult, +} from '@tanstack/react-query'; +import { useRouter } from 'next/navigation'; +import { useEffect, useRef } from 'react'; +import { useShallow } from 'zustand/shallow'; +import { AlbumDetailMode } from './ScreenAlbumDetail'; + +const SELECT_MODE_MIN_HEIGHT = '800px'; + +export const ID_PHOTO_LIST = 'photo-list'; +export const ID_PHOTO_LIST_ANCHOR = 'photo-list-anchor'; + +interface PhotoListProps { + albumId: string; + selectable?: boolean; + changeMode: (newMode: AlbumDetailMode) => void; + mode: AlbumDetailMode; + photos: PhotoListResponseSchema[]; + fetchNextPage: ( + options?: FetchNextPageOptions, + ) => Promise; + hasNextPage: boolean; + isFetchingNextPage: boolean; + totalPhotoCount?: number; +} + +export default function PhotoList({ + albumId, + selectable = false, + changeMode, + mode, + photos, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + totalPhotoCount, +}: PhotoListProps) { + const router = useRouter(); + const photoListRef = useRef(null); + const anchorRef = useRef(null); + const loadMoreRef = useRef(null); + const { addSelectedPhoto, deleteSelectedPhoto, isSelected } = + useSelectedPhotosStore( + useShallow((state) => ({ + selectedPhotos: state.selectedPhotos, + addSelectedPhoto: state.addSelectedPhoto, + deleteSelectedPhoto: state.deleteSelectedPhoto, + clearSelectedPhotos: state.clearSelectedPhotos, + isSelected: state.isSelected, + })), + ); + const { sortType, setSortType } = useAlbumSortStore( + useShallow((state) => ({ + sortType: state.sortType, + setSortType: state.setSortType, + })), + ); + const { albumType, setAlbumType } = useAlbumTypeStore( + useShallow((state) => ({ + albumType: state.albumType, + setAlbumType: state.setAlbumType, + })), + ); + + useEffect(() => { + if (!hasNextPage) return; + const target = loadMoreRef.current; + if (!target) return; + + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, + { + rootMargin: '200px 0px', + }, + ); + + observer.observe(target); + + return () => { + observer.disconnect(); + }; + }, [fetchNextPage, hasNextPage, isFetchingNextPage]); + + const handlePhotoPress = ({ + photoId, + photoUrl, + }: { + photoId: number; + photoUrl: string; + }): void => { + if (!selectable) return; + + if (isSelected(photoId)) { + deleteSelectedPhoto(photoId); + } else { + addSelectedPhoto({ id: photoId, url: photoUrl }); + } + }; + + const handleChangeMode = (nextMode: AlbumDetailMode): void => { + const photoListEl = photoListRef.current; + const anchorEl = anchorRef.current; + + if (nextMode === 'select') { + if (photoListEl) { + photoListEl.style.minHeight = SELECT_MODE_MIN_HEIGHT; + } + + if (anchorEl) { + anchorEl.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + } else { + window.scrollTo({ top: 0, behavior: 'smooth' }); + + // behavior:smooth동작 끝나면 min-height 원복 + setTimeout(() => { + if (photoListEl) { + photoListEl.style.minHeight = ''; + } + }, 300); + } + + changeMode(nextMode); + }; + + return ( +
+
+
+ + 총 {(albumType === 'all' ? totalPhotoCount : photos.length) || 0}장 + + {mode === 'default' && ( + + )} + {mode === 'select' && ( + + )} +
+
+ {photos.map( + ({ + photoId, + likeCnt, + isLiked, + thumbnailUrl, + imageUrl, + isDownloaded, + isRecentlyDownloaded, + }) => { + if (!photoId || !thumbnailUrl || !imageUrl) { + return null; + } + + return ( + { + if (mode === 'default') { + router.push( + `/photo/detail/${albumId}${buildQuery({ photoId: photoId })}`, + ); + } else { + handlePhotoPress({ photoId, photoUrl: imageUrl }); + } + }} + onDisabledPress={() => { + if (mode === 'default') { + router.push( + `/photo/detail/${albumId}${buildQuery({ photoId: photoId })}`, + ); + } else { + Toast.alert( + `금방 다운받은 사진이에요.\n1시간 뒤에 다시 시도하세요.`, + ); + } + }} + /> + ); + }, + )} +
+
+
+ ); +} diff --git a/src/feature/album/detail/components/ScreenAlbumDetail.tsx b/src/feature/album/detail/components/ScreenAlbumDetail.tsx new file mode 100644 index 00000000..0c4485bf --- /dev/null +++ b/src/feature/album/detail/components/ScreenAlbumDetail.tsx @@ -0,0 +1,227 @@ +'use client'; + +import EmojiLoading from '@/components/ui/EmojiLoading'; +import { useAlbumPhotosInfiniteQuery } from '@/feature/photo-detail/hooks/useAlbumPhotosInfiniteQuery'; +import { + useAlbumPhotosLikedInfiniteQuery, + type AlbumPhotosLikedItem, +} from '@/feature/photo-detail/hooks/useAlbumPhotosLikedInfiniteQuery'; +import { PhotoListResponseSchema } from '@/global/api/ep'; +import CustomHeader, { + HEADER_HEIGHT, +} from '@/global/components/header/CustomHeader'; +import { DEFAULT_PROFILE_IMAGE } from '@/global/constants/images'; +import { useAlbumSortStore } from '@/store/useAlbumSortStore'; +import { useAlbumTypeStore } from '@/store/useAlbumTypeStore'; +import { useSelectedPhotosStore } from '@/store/useSelectedPhotosStore'; +import { useUploadingStore } from '@/store/useUploadingStore'; +import { Menu } from 'lucide-react'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { useShallow } from 'zustand/shallow'; +import { photoSortToApiSorting } from '../constants/photoSort'; +import { useGetAlbumAvailableCount } from '../hooks/useGetAlbumAvailableCount'; +import { useGetAlbumInvitation } from '../hooks/useGetAlbumInvitation'; +import AlbumBottomActions from './AlbumBottomActions'; +import AlbumInfos from './AlbumInfos'; +import AlbumPhotoSection from './AlbumPhotoSection'; + +export type AlbumDetailMode = 'select' | 'default'; + +interface ScreenAlbumDetailProps { + albumId: string; +} + +const LOADING_MODAL_DURATION = 5000; + +export default function ScreenAlbumDetail({ albumId }: ScreenAlbumDetailProps) { + const router = useRouter(); + const albumInfosRef = useRef(null); + const [mode, setMode] = useState('default'); + const [isAlbumInfosHidden, setIsAlbumInfosHidden] = useState(false); + const [selectionResetKey, setSelectionResetKey] = useState(0); + const { sortType, setSortType } = useAlbumSortStore( + useShallow((state) => ({ + sortType: state.sortType, + setSortType: state.setSortType, + })), + ); + const { albumType, setAlbumType } = useAlbumTypeStore( + useShallow((state) => ({ + albumType: state.albumType, + setAlbumType: state.setAlbumType, + })), + ); + const sorting = photoSortToApiSorting[sortType]; + const { + selectedPhotos, + addSelectedPhoto, + deleteSelectedPhoto, + clearSelectedPhotos, + } = useSelectedPhotosStore( + useShallow((state) => ({ + selectedPhotos: state.selectedPhotos, + addSelectedPhoto: state.addSelectedPhoto, + deleteSelectedPhoto: state.deleteSelectedPhoto, + clearSelectedPhotos: state.clearSelectedPhotos, + })), + ); + const { isUploaded, setUploaded } = useUploadingStore( + useShallow((state) => ({ + isUploaded: state.isUploaded, + setUploaded: state.setUploaded, + })), + ); + + const { + data: invitationData, + isLoading: isInvitationLoading, + isError: isInvitationError, + } = useGetAlbumInvitation(albumId); + const isDeepAlbumType = albumType === 'deep'; + const { data } = useGetAlbumAvailableCount(albumId); + const totalPhotoCount = data?.currentPhotoCount; + const defaultPhotosQuery = useAlbumPhotosInfiniteQuery({ + code: albumId, + sorting, + enabled: albumType === 'all', + // 좋아요 누른것 실시간으로 반영되게 매번 호출 + refetchOnMount: 'always', + }); + + const likedPhotosQuery = useAlbumPhotosLikedInfiniteQuery({ + code: albumId, + enabled: isDeepAlbumType, + // 좋아요 누른것 실시간으로 반영되게 매번 호출 + refetchOnMount: 'always', + }); + + const likedPhotos = useMemo( + () => mapLikedPhotosToPhotoList(likedPhotosQuery.items), + [likedPhotosQuery.items], + ); + + const photos: PhotoListResponseSchema[] = isDeepAlbumType + ? likedPhotos + : defaultPhotosQuery.items; + const fetchNextPage = isDeepAlbumType + ? likedPhotosQuery.fetchNextPage + : defaultPhotosQuery.fetchNextPage; + const hasNextPage = isDeepAlbumType + ? likedPhotosQuery.hasNextPage + : defaultPhotosQuery.hasNextPage; + const isFetchingNextPage = isDeepAlbumType + ? likedPhotosQuery.isFetchingNextPage + : defaultPhotosQuery.isFetchingNextPage; + const isLoading = defaultPhotosQuery.isLoading; + + useEffect(() => { + const target = albumInfosRef.current; + if (!target) return; + + const observer = new IntersectionObserver( + ([entry]) => { + setIsAlbumInfosHidden(!entry.isIntersecting); + }, + { + rootMargin: `-${HEADER_HEIGHT}px 0px 0px 0px`, + }, + ); + + observer.observe(target); + + return () => { + observer.disconnect(); + }; + }, []); + + useEffect(() => { + if (mode === 'select') return; + if (selectedPhotos.length === 0) return; + + clearSelectedPhotos(); + setSelectionResetKey((prev) => prev + 1); + }, [clearSelectedPhotos, photos.length, mode, selectedPhotos.length]); + + useEffect(() => { + return () => { + clearSelectedPhotos(); + }; + }, [clearSelectedPhotos]); + + const handleChangeMode = (newMode: AlbumDetailMode) => setMode(newMode); + + const emoji = invitationData?.themeEmoji; + + return ( + <> + {isUploaded && ( + + )} + router.replace('/main')} + isHidden={mode === 'select'} + title={isAlbumInfosHidden ? (invitationData?.title ?? '') : ''} + rightContent={ +
+ + + +
+ } + /> +
+ + +
+ + + ); +} + +function mapLikedPhotosToPhotoList( + items: AlbumPhotosLikedItem[], +): PhotoListResponseSchema[] { + return items.map((item) => ({ + name: item.name, + photoId: item.photoId, + imageUrl: item.imageUrl, + thumbnailUrl: item.thumbnailUrl, + profileImage: DEFAULT_PROFILE_IMAGE, // 사용되지 않는필드. 타입을위해 임시 DEFAULT 프사 넣음. + likeCnt: item.likeCnt ?? 0, + isLiked: item.isLiked ?? false, + isDownloaded: item.isDownloaded, + isRecentlyDownloaded: item.isRecentlyDownloaded, + })); +} diff --git a/src/feature/album/detail/components/SelectPhotoSortType.tsx b/src/feature/album/detail/components/SelectPhotoSortType.tsx new file mode 100644 index 00000000..e1ccd0b3 --- /dev/null +++ b/src/feature/album/detail/components/SelectPhotoSortType.tsx @@ -0,0 +1,48 @@ +import { DrawerClose } from '@/components/ui/drawer'; +import { Check } from 'lucide-react'; +import type { PhotoSortType } from '../constants/photoSort'; +import { photoSortOptions } from '../constants/photoSort'; + +interface SelectPhotoSortTypeProps { + sort: PhotoSortType; + onChange: (newType: PhotoSortType) => void; +} + +export default function SelectPhotoSortType({ + sort, + onChange, +}: SelectPhotoSortTypeProps) { + return ( +
+
    + {photoSortOptions.map((item) => { + const isActive = sort === item.value; + return ( +
  • + { + onChange(item.value); + }} + className='flex w-full items-center gap-2.5 py-4' + > + + {isActive && ( + + )} + + + {item.label} + + +
  • + ); + })} +
+
+ ); +} diff --git a/src/feature/album/detail/components/UploadButtonInDetail.tsx b/src/feature/album/detail/components/UploadButtonInDetail.tsx new file mode 100644 index 00000000..b243ce57 --- /dev/null +++ b/src/feature/album/detail/components/UploadButtonInDetail.tsx @@ -0,0 +1,68 @@ +'use client'; + +import { handleFileUpload } from '@/feature/create-album/utils/handleFileUpload'; +import { EP } from '@/global/api/ep'; +import LongButton from '@/global/components/LongButton'; +import Toast from '@/global/components/toast/Toast'; +import { useUploadingStore } from '@/store/useUploadingStore'; +import { useQueryClient } from '@tanstack/react-query'; +import { useParams, useRouter } from 'next/navigation'; +import { useRef } from 'react'; + +interface UploadButtonInDetailProps { + buttonText?: string; +} + +export default function UploadButtonInDetail({ + buttonText, +}: UploadButtonInDetailProps) { + const fileInputRef = useRef(null); + const isUploaded = useUploadingStore((state) => state.isUploaded); + const router = useRouter(); + const params = useParams(); + const queryClient = useQueryClient(); + const albumId = + typeof params.albumId === 'string' + ? params.albumId + : Array.isArray(params.albumId) + ? params.albumId[0] + : ''; + + const onFileChange = async (e: React.ChangeEvent) => { + if (!e.target.files || e.target.files.length === 0) return; + const { success } = await handleFileUpload(e, albumId, router, { + stay: true, + }); + + await queryClient.invalidateQueries({ + queryKey: [EP.album.photos(albumId)], + }); + + if (success) { + setTimeout(() => Toast.check(`총 ${success}장을 앨범에 채웠어요.`), 2000); + } + }; + + const handleButtonClick = () => { + if (!isUploaded) fileInputRef.current?.click(); + }; + + return ( + <> + + + + ); +} diff --git a/src/feature/album/detail/constants/photoSort.ts b/src/feature/album/detail/constants/photoSort.ts new file mode 100644 index 00000000..60e3e73e --- /dev/null +++ b/src/feature/album/detail/constants/photoSort.ts @@ -0,0 +1,15 @@ +import { PhotoSorting } from '@/global/api/ep'; + +export type PhotoSortType = 'uploaded' | 'shot' | 'liked'; + +export const photoSortOptions: { value: PhotoSortType; label: string }[] = [ + { value: 'uploaded', label: '최근 업로드된 사진순' }, + { value: 'shot', label: '최근 촬영한 시간순' }, + { value: 'liked', label: '띱 많은 순' }, +]; + +export const photoSortToApiSorting: Record = { + uploaded: 'CREATED_AT', + shot: 'CAPTURED_AT', + liked: 'POPULAR', +}; diff --git a/src/feature/album/detail/hooks/useGet4CutPreview.ts b/src/feature/album/detail/hooks/useGet4CutPreview.ts new file mode 100644 index 00000000..eb404159 --- /dev/null +++ b/src/feature/album/detail/hooks/useGet4CutPreview.ts @@ -0,0 +1,25 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; + +const fetchData = async (albumId: string) => { + const response = await api.get({ + path: EP.cheese4cut.preview(albumId), + }); + + return response.result; +}; + +export function useGet4CutPreview( + albumId: string, + options?: UseQueryOptions, +) { + const query = useQuery({ + queryKey: [EP.cheese4cut.preview(albumId)], + queryFn: () => fetchData(albumId), + enabled: !!albumId, + ...options, + }); + + return query; +} diff --git a/src/feature/album/detail/hooks/useGetAlbumAvailableCount.ts b/src/feature/album/detail/hooks/useGetAlbumAvailableCount.ts new file mode 100644 index 00000000..911c13c9 --- /dev/null +++ b/src/feature/album/detail/hooks/useGetAlbumAvailableCount.ts @@ -0,0 +1,25 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; + +const fetchData = async (albumId: string) => { + const response = await api.get({ + path: EP.album.availableCount(albumId), + }); + + return response.result; +}; + +export function useGetAlbumAvailableCount( + albumId: string, + options?: UseQueryOptions, +) { + const query = useQuery({ + queryKey: [EP.album.availableCount(albumId)], + queryFn: () => fetchData(albumId), + enabled: !!albumId, + ...options, + }); + + return query; +} diff --git a/src/feature/album/detail/hooks/useGetAlbumInfo.ts b/src/feature/album/detail/hooks/useGetAlbumInfo.ts new file mode 100644 index 00000000..91a30942 --- /dev/null +++ b/src/feature/album/detail/hooks/useGetAlbumInfo.ts @@ -0,0 +1,25 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; + +const fetchData = async (albumId: string) => { + const response = await api.get({ + path: EP.album.albumInfo(albumId), + }); + + return response.result; +}; + +export function useGetAlbumInfo( + albumId: string, + options?: UseQueryOptions, +) { + const query = useQuery({ + queryKey: [EP.album.albumInfo(albumId)], + queryFn: () => fetchData(albumId), + enabled: !!albumId, + ...options, + }); + + return query; +} diff --git a/src/feature/album/detail/hooks/useGetAlbumInvitation.ts b/src/feature/album/detail/hooks/useGetAlbumInvitation.ts new file mode 100644 index 00000000..cc37e5db --- /dev/null +++ b/src/feature/album/detail/hooks/useGetAlbumInvitation.ts @@ -0,0 +1,22 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; + +const getAlbumInvitation = async (code: string) => { + const response = await api.get({ + path: EP.album.invitation(code), + }); + return response.result; +}; + +export function useGetAlbumInvitation( + albumId: string, + options?: UseQueryOptions, +) { + return useQuery({ + queryKey: [EP.album.invitation(albumId)], + queryFn: () => getAlbumInvitation(albumId), + enabled: !!albumId, + ...options, + }); +} diff --git a/src/feature/album/detail/hooks/useGetPhotoListByAlbumId.server.ts b/src/feature/album/detail/hooks/useGetPhotoListByAlbumId.server.ts new file mode 100644 index 00000000..83901d93 --- /dev/null +++ b/src/feature/album/detail/hooks/useGetPhotoListByAlbumId.server.ts @@ -0,0 +1,22 @@ +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; +import { + getPhotoListByAlbumId, + GetPhotoListParams, +} from '../api/getPhotoListByAlbumId.server'; + +export type PhotoListResponse = Awaited< + ReturnType +>; + +export function useGetPhotoListByAlbumId( + albumId: string, + params?: GetPhotoListParams, + options?: UseQueryOptions, +) { + return useQuery({ + queryKey: ['albumPhotos', albumId, params], + queryFn: () => getPhotoListByAlbumId(albumId, params), + enabled: !!albumId, + ...options, + }); +} diff --git a/src/feature/album/detail/sidebar/components/AlbumParticipants.tsx b/src/feature/album/detail/sidebar/components/AlbumParticipants.tsx new file mode 100644 index 00000000..4971fe47 --- /dev/null +++ b/src/feature/album/detail/sidebar/components/AlbumParticipants.tsx @@ -0,0 +1,54 @@ +import { useGetAlbumInform } from '@/feature/upload/hooks/useGetAlbumInform'; +import BottomSheetModal from '@/global/components/modal/BottomSheetModal'; +import BottomSheetContentShare from './BottomSheetContentShare'; +import ItemParticipant from './ItemParticipant'; + +interface AlbumParticipantsProps { + albumId: string; +} + +export default function AlbumParticipants({ albumId }: AlbumParticipantsProps) { + const { data, isPending, isError } = useGetAlbumInform({ code: albumId }); + + if (isPending) return null; + if (isError) return null; + if (!data) return null; + + return ( +
+
+
+

+ 앨범 참가자 + {`${data.currentParticipantCount}/${data.maxParticipantCount}`} +

+
+ {!data.isExpired && ( + + 친구 초대 + + } + > + + + )} +
+
+ {data.participants?.map(({ isMe, name, profileImage, role }, index) => ( + + ))} +
+
+ ); +} diff --git a/src/feature/album/detail/sidebar/components/BottomSheetContentShare.tsx b/src/feature/album/detail/sidebar/components/BottomSheetContentShare.tsx new file mode 100644 index 00000000..ed1c9141 --- /dev/null +++ b/src/feature/album/detail/sidebar/components/BottomSheetContentShare.tsx @@ -0,0 +1,31 @@ +import CopyShareButton from './CopyShareButton'; +import KakaoShareButton from './KakaoShareButton'; +import MoreShareButton from './MoreShareButton'; +import QrcodeShareButton from './QrcodeShareButton'; + +interface BottomSheetContentShareProps { + albumId: string; +} + +export default function BottomSheetContentShare({ + albumId, +}: BottomSheetContentShareProps) { + return ( +
+
+

+ 친구 초대하기 +

+ + 사진이 채워지는 동안 친구에게 앨범을 공유해보세요. + +
+
+ + + + +
+
+ ); +} diff --git a/src/feature/album/detail/sidebar/components/CopyShareButton.tsx b/src/feature/album/detail/sidebar/components/CopyShareButton.tsx new file mode 100644 index 00000000..ce35018b --- /dev/null +++ b/src/feature/album/detail/sidebar/components/CopyShareButton.tsx @@ -0,0 +1,29 @@ +import Toast from '@/global/components/toast/Toast'; +import { copyToClipboard } from '@/global/utils/copyToClipboard'; +import { Copy } from 'lucide-react'; + +interface CopyShareButtonProps { + albumId: string; +} + +export default function CopyShareButton({ albumId }: CopyShareButtonProps) { + const handleClick = (): void => { + copyToClipboard( + `${process.env.NEXT_PUBLIC_CLIENT_URL}/album/entry/${albumId}`, + ); + Toast.check('링크를 복사했어요'); + }; + + return ( + + ); +} diff --git a/src/feature/album/detail/sidebar/components/ItemParticipant.tsx b/src/feature/album/detail/sidebar/components/ItemParticipant.tsx new file mode 100644 index 00000000..55a983f9 --- /dev/null +++ b/src/feature/album/detail/sidebar/components/ItemParticipant.tsx @@ -0,0 +1,42 @@ +import { DEFAULT_PROFILE_IMAGE } from '@/global/constants/images'; + +interface ItemParticipantProps { + name: string; + profileImage?: string; + role?: 'MAKER' | 'GUEST' | 'BLACK'; + isMe?: boolean; +} + +export default function ItemParticipant({ + profileImage, + name, + isMe, + role, +}: ItemParticipantProps) { + return ( +
+
+ 프로필사진 +
+ {name} + {role === 'MAKER' && ( + + 메이커 + + )} + {isMe && ( + + 나 + + )} +
+
+
+ ); +} diff --git a/src/feature/album/detail/sidebar/components/KakaoShareButton.tsx b/src/feature/album/detail/sidebar/components/KakaoShareButton.tsx new file mode 100644 index 00000000..4195888d --- /dev/null +++ b/src/feature/album/detail/sidebar/components/KakaoShareButton.tsx @@ -0,0 +1,36 @@ +import { shareKakao } from '@/global/utils/shareKakao'; +import Image from 'next/image'; + +interface KakaoShareButtonProps { + albumId: string; +} + +export default function KakaoShareButton({ albumId }: KakaoShareButtonProps) { + const handleClick = (): void => { + shareKakao({ + title: '앨범에 초대해요', + description: '치이이즈: 추억은 따끈할 때 제맛', + imageUrl: `https://say-cheese.me/assets/og/og_kakao.png`, + imageWidth: 1200, + imageHeight: 630, + link: `https://say-cheese.me/album/entry/${albumId}`, + }); + }; + + return ( + + ); +} diff --git a/src/feature/album/detail/sidebar/components/MoreShareButton.tsx b/src/feature/album/detail/sidebar/components/MoreShareButton.tsx new file mode 100644 index 00000000..0e8826b2 --- /dev/null +++ b/src/feature/album/detail/sidebar/components/MoreShareButton.tsx @@ -0,0 +1,37 @@ +import { shareViaNavigator } from '@/global/utils/shareNavigator'; +import { Ellipsis } from 'lucide-react'; + +const getAlbumEntryUrl = (albumId: string) => + `${process.env.NEXT_PUBLIC_CLIENT_URL}/album/entry/${albumId}`; + +interface MoreShareButtonProps { + albumId: string; +} + +export default function MoreShareButton({ albumId }: MoreShareButtonProps) { + const handleClick = (): void => { + const shareData = { + title: `우리 공유앨범에 초대합니다 - 치이이즈`, + text: '일주일 뒤에는 앨범이 사라져요!', + url: getAlbumEntryUrl(albumId), + }; + + shareViaNavigator({ + data: shareData, + fallbackMessage: '이 기능을 지원하지 않는 브라우저입니다.', + }); + }; + + return ( + + ); +} diff --git a/src/feature/album/detail/sidebar/components/QrcodeShareButton.tsx b/src/feature/album/detail/sidebar/components/QrcodeShareButton.tsx new file mode 100644 index 00000000..84e74cd3 --- /dev/null +++ b/src/feature/album/detail/sidebar/components/QrcodeShareButton.tsx @@ -0,0 +1,27 @@ +import { QrCode } from 'lucide-react'; +import { useRouter } from 'next/navigation'; + +interface QrcodeShareButtonProps { + albumId: string; +} + +export default function QrcodeShareButton({ albumId }: QrcodeShareButtonProps) { + const router = useRouter(); + + const handleClick = (): void => { + router.push(`/album/qrcode/${albumId}`); + }; + + return ( + + ); +} diff --git a/src/feature/album/detail/sidebar/components/ScreenAlbumSidebar.tsx b/src/feature/album/detail/sidebar/components/ScreenAlbumSidebar.tsx new file mode 100644 index 00000000..ccedaaa3 --- /dev/null +++ b/src/feature/album/detail/sidebar/components/ScreenAlbumSidebar.tsx @@ -0,0 +1,96 @@ +'use client'; + +import { HEADER_HEIGHT } from '@/global/components/header/CustomHeader'; +import ConfirmModal from '@/global/components/modal/ConfirmModal'; +import Toast from '@/global/components/toast/Toast'; +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; +import { + formatExpirationTime, + getIsExpired, +} from '@/global/utils/time/formatExpirationTime'; +import { X } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { useGetAlbumInfo } from '../../hooks/useGetAlbumInfo'; +import { useAlbumExitMutation } from '../hooks/useAlbumExitMutation'; +import AlbumParticipants from './AlbumParticipants'; + +interface ScreenAlbumSidebarProps { + albumId: string; +} + +export default function ScreenAlbumSidebar({ + albumId, +}: ScreenAlbumSidebarProps) { + const router = useRouter(); + const { data, isPending, isError } = useGetAlbumInfo(albumId); + const { mutateAsync } = useAlbumExitMutation(); + + if (isPending) return null; + if (isError) return null; + + const handleExit = async (): Promise => { + try { + await mutateAsync(albumId); + router.replace('/main'); + Toast.check(`${data?.title ? `${data.title} ` : ''}앨범이 삭제됐어요.`); + } catch (e) { + console.log(e); + Toast.alert(`앨범 삭제를 실패하였어요.\n다시한번 시도해주세요.`); + } + }; + const isExpired = getIsExpired(data?.expiredAt); + + return ( + <> +
+
+ +
+ {data?.themeEmoji ? convertUnicodeToEmoji(data?.themeEmoji) : '😀'} +
+

+ {data?.title} +

+

+ {data?.eventDate} +

+ {!isExpired && ( +
+ 앨범 소멸까지 {formatExpirationTime(data?.expiredAt)} +
+ )} +
+ + + +
+ + 앨범 나가기 + + } + title='앨범에서 나갈까요?' + description='나가더라도 내가 올린 사진은 앨범에 남아요.' + cancelText='다음에' + confirmText='앨범 나가기' + confirmClassName='bg-button-accent-fill text-white active:bg-button-accent-pressed active:text-basic-inverse' + onConfirm={handleExit} + /> +
+
+ + ); +} diff --git a/src/feature/album/detail/sidebar/hooks/useAlbumExitMutation.ts b/src/feature/album/detail/sidebar/hooks/useAlbumExitMutation.ts new file mode 100644 index 00000000..9e42e61e --- /dev/null +++ b/src/feature/album/detail/sidebar/hooks/useAlbumExitMutation.ts @@ -0,0 +1,18 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +async function fetchData(albumId: string) { + const res = await api.delete({ + path: EP.album.albumParticipantsMe(albumId), + }); + return res.result; +} + +export function useAlbumExitMutation() { + const mutation = useMutation({ + mutationFn: (albumId: string) => fetchData(albumId), + }); + + return mutation; +} diff --git a/src/feature/album/qrcode/components/CardAlbumQrcode.tsx b/src/feature/album/qrcode/components/CardAlbumQrcode.tsx new file mode 100644 index 00000000..3738ee5f --- /dev/null +++ b/src/feature/album/qrcode/components/CardAlbumQrcode.tsx @@ -0,0 +1,38 @@ +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; +import QRCode from 'react-qr-code'; +import { useGetAlbumInfo } from '../../detail/hooks/useGetAlbumInfo'; + +interface CardAlbumQrcodeProps { + albumId: string; +} + +export default function CardAlbumQrcode({ albumId }: CardAlbumQrcodeProps) { + const { data, isPending, isError } = useGetAlbumInfo(albumId); + + if (isPending) return null; + if (isError) return null; + if (!data) return null; + + const qrValue = `${process.env.NEXT_PUBLIC_CLIENT_URL}/album/entry/${albumId}`; + + return ( +
+
+ {data.themeEmoji ? convertUnicodeToEmoji(data.themeEmoji) : '😀'} +
+

+ {data.title} +

+

+ {data.eventDate} +

+
+ +
+
+ ); +} diff --git a/src/feature/album/qrcode/components/ScreenAlbumQrcode.tsx b/src/feature/album/qrcode/components/ScreenAlbumQrcode.tsx new file mode 100644 index 00000000..9c15380f --- /dev/null +++ b/src/feature/album/qrcode/components/ScreenAlbumQrcode.tsx @@ -0,0 +1,27 @@ +'use client'; +import { X } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import CardAlbumQrcode from './CardAlbumQrcode'; + +interface ScreenAlbumQrcodeProps { + albumId: string; +} + +export default function ScreenAlbumQrcode({ albumId }: ScreenAlbumQrcodeProps) { + const router = useRouter(); + + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/feature/create-album/api/checkAvailableCount.server.ts b/src/feature/create-album/api/checkAvailableCount.server.ts new file mode 100644 index 00000000..264da435 --- /dev/null +++ b/src/feature/create-album/api/checkAvailableCount.server.ts @@ -0,0 +1,15 @@ +import { serverApi } from '@/global/utils/serverApi'; + +/** + * 앨범에 업로드 가능한 남은 이미지 개수 조회 (Server Component용) + * @param albumId 앨범 ID + * @returns {Promise} 남은 업로드 가능 개수 + */ +export async function checkAvailableCountServer( + albumId: string, +): Promise { + const response = await serverApi.get<{ availableCount: number }>({ + path: `/v1/album/${albumId}/available-count`, + }); + return response.result.availableCount; +} diff --git a/src/feature/create-album/api/checkAvailableCount.ts b/src/feature/create-album/api/checkAvailableCount.ts new file mode 100644 index 00000000..707939aa --- /dev/null +++ b/src/feature/create-album/api/checkAvailableCount.ts @@ -0,0 +1,13 @@ +import { api } from '@/global/utils/api'; + +/** + * 앨범에 업로드 가능한 남은 이미지 개수 조회 (Client Component용) + * @param albumId 앨범 ID + * @returns {Promise} 남은 업로드 가능 개수 + */ +export async function checkAvailableCount(albumId: string): Promise { + const response = await api.get<{ availableCount: number }>({ + path: `/v1/album/${albumId}/available-count`, + }); + return response.result.availableCount; +} diff --git a/src/feature/create-album/api/createAlbumApi.ts b/src/feature/create-album/api/createAlbumApi.ts new file mode 100644 index 00000000..af70cb4b --- /dev/null +++ b/src/feature/create-album/api/createAlbumApi.ts @@ -0,0 +1,33 @@ +import { api } from '../../../global/utils/api'; + +export interface CreateAlbumRequest { + themeEmoji: string; // U+1F9C0 형식의 유니코드 + title: string; + participant: number; + eventDate: string; // YYYY-MM-DD 형식 +} + +export interface CreateAlbumResponse { + themeEmoji: string; + title: string; + eventDate: string; + currentPhotoCnt: number; + code: string; +} + +/** + * 앨범 생성 API + * @param data 앨범 생성 요청 데이터 + * @returns API 응답 전체 (isSuccess, code, message, result 포함) + */ +export async function createAlbumApi(data: CreateAlbumRequest) { + return await api.post({ + path: '/v1/album', + body: { + themeEmoji: data.themeEmoji, + title: data.title, + participant: data.participant, + eventDate: data.eventDate, + }, + }); +} diff --git a/src/feature/create-album/components/AlbumEmojiSelector.tsx b/src/feature/create-album/components/AlbumEmojiSelector.tsx new file mode 100644 index 00000000..0ccdda7c --- /dev/null +++ b/src/feature/create-album/components/AlbumEmojiSelector.tsx @@ -0,0 +1,100 @@ +'use client'; +import { Pencil } from 'lucide-react'; +import dynamic from 'next/dynamic'; +import React, { useEffect, useRef, useState } from 'react'; + +const EmojiPicker = dynamic(() => import('emoji-picker-react'), { + ssr: false, + loading: () => ( +
+ ), +}); + +interface EmojiClickData { + emoji: string; + unified: string; +} + +interface AlbumEmojiSelectorProps { + selectedEmoji: string; + onEmojiSelect: (emoji: string) => void; +} + +const AlbumEmojiSelector = React.memo(function AlbumEmojiSelector({ + selectedEmoji, + onEmojiSelect, +}: AlbumEmojiSelectorProps) { + const [showPicker, setShowPicker] = useState(false); + const pickerRef = useRef(null); + const containerRef = useRef(null); + + const handleEmojiClick = (emojiData: EmojiClickData) => { + onEmojiSelect(emojiData.emoji); + setShowPicker(false); + }; + + useEffect(() => { + if (!showPicker) return; + const handleClick = (e: MouseEvent) => { + if ( + (pickerRef.current && pickerRef.current.contains(e.target as Node)) || + (containerRef.current && + containerRef.current.contains(e.target as Node)) + ) { + return; + } + setShowPicker(false); + }; + document.addEventListener('mousedown', handleClick); + return () => { + document.removeEventListener('mousedown', handleClick); + }; + }, [showPicker]); + + return ( +
+ {/* 이모지 표시 */} +
{ + e.stopPropagation(); + setShowPicker((v) => !v); + }} + > +
+ {selectedEmoji} +
+ +
+ + {/* 이모지 피커 (fixed로 고정) */} + {showPicker && ( +
+ +
+ )} +
+ ); +}); + +export default AlbumEmojiSelector; diff --git a/src/feature/create-album/components/CreateAlbumList.tsx b/src/feature/create-album/components/CreateAlbumList.tsx new file mode 100644 index 00000000..f106abb9 --- /dev/null +++ b/src/feature/create-album/components/CreateAlbumList.tsx @@ -0,0 +1,114 @@ +'use client'; +import LongButton from '@/global/components/LongButton'; +import dynamic from 'next/dynamic'; +const BottomSheetModal = dynamic( + () => import('@/global/components/modal/BottomSheetModal'), + { ssr: false }, +); +import { useRouter } from 'next/navigation'; +import { useState } from 'react'; +import { + useCreateAlbum, + type CreateAlbumApiResponse, + type CreateAlbumError, +} from '../hook/useCreateAlbum'; + +import Toast from '@/global/components/toast/Toast'; +import AlbumEmojiSelector from './AlbumEmojiSelector'; +import CreateInputList from './CreateInputList'; + +// 이모지를 유니코드 코드포인트 형식으로 변환 (예: 😊 → U+1F60A) +const emojiToUnicode = (emoji: string): string => { + const codePoint = emoji.codePointAt(0); + if (!codePoint) return ''; + return `U+${codePoint.toString(16).toUpperCase().padStart(4, '0')}`; +}; + +export default function CreateAlbumList() { + const router = useRouter(); + const [selectedEmoji, setSelectedEmoji] = useState('😊'); + const [eventName, setEventName] = useState(''); + const [eventDate, setEventDate] = useState(''); + const [participantCount, setParticipantCount] = useState(''); + const [hasFormError, setHasFormError] = useState(false); + + const { mutate: createAlbum } = useCreateAlbum(); + + const handleSubmit = () => { + const emojiUnicode = emojiToUnicode(selectedEmoji); + createAlbum( + { + themeEmoji: emojiUnicode, + title: eventName, + participant: parseInt(participantCount, 10), + eventDate, + }, + { + onSuccess: (result: CreateAlbumApiResponse) => { + if (result.result.code) { + router.push(`/create-album/${result.result.code}/complete`); + } + }, + onError: (err: CreateAlbumError) => { + Toast.alert(err.message || '앨범 생성에 실패했습니다.'); + console.error('앨범 생성 실패:', err); + }, + }, + ); + }; + + const participantCountNumber = parseInt(participantCount, 10); + const isFormComplete = + eventName.trim() !== '' && + eventDate.trim() !== '' && + participantCount.trim() !== '' && + participantCountNumber >= 1 && + participantCountNumber <= 64 && + !hasFormError; + + return ( +
+ + + + } + showCloseButton={false} + className='pt-10 pb-5 pl-6' + dismissible={true} + showHandle={false} + > +
+ + 치즈 앨범 메뉴얼 + +
    +
  • • 이 앨범은 7일 뒤 자동으로 사라져요.
  • +
  • • 메이커는 규칙을 어긴 참여자를 내보낼 수 있어요.
  • +
  • • 메이커는 모든 사진을 정리 • 삭제할 수 있어요
  • +
+
+ +
+
+ ); +} diff --git a/src/feature/create-album/components/CreateComplete.tsx b/src/feature/create-album/components/CreateComplete.tsx new file mode 100644 index 00000000..cb924b3c --- /dev/null +++ b/src/feature/create-album/components/CreateComplete.tsx @@ -0,0 +1,32 @@ +'use client'; +import { CountdownTimer } from '@/global/components/CountdownTimer'; +import LongButton from '@/global/components/LongButton'; +import { Check } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { useCallback } from 'react'; + +interface CreateCompleteProps { + albumId: string; +} + +export default function CreateComplete({ albumId }: CreateCompleteProps) { + const router = useRouter(); + + const handleClick = useCallback(() => { + router.push(`/album/upload/${albumId}`); + }, [router, albumId]); + + return ( +
+
+ +
+
+ 만들기 성공! + 앨범이 열렸어요 +
+ + +
+ ); +} diff --git a/src/feature/create-album/components/CreateInputList.tsx b/src/feature/create-album/components/CreateInputList.tsx new file mode 100644 index 00000000..baab1128 --- /dev/null +++ b/src/feature/create-album/components/CreateInputList.tsx @@ -0,0 +1,114 @@ +'use client'; + +import DateXInput from '@/global/components/DateXInput'; +import XInput from '@/global/components/XInput'; +import { useState } from 'react'; + +interface CreateInputListProps { + eventName: string; + eventDate: string; + participantCount: string; + onEventNameChange: (value: string) => void; + onEventDateChange: (value: string) => void; + onParticipantCountChange: (value: string) => void; + onErrorChange?: (hasError: boolean) => void; +} + +export default function CreateInputList({ + eventName, + eventDate, + participantCount, + onEventNameChange, + onEventDateChange, + onParticipantCountChange, + onErrorChange, +}: CreateInputListProps) { + const [eventNameError, setEventNameError] = useState(''); + const [participantCountError, setParticipantCountError] = useState(''); + + // 이벤트 이름 검증: 13글자 이내의 한글, 영문, 숫자, _, .만 허용 + const handleEventNameChange = (value: string) => { + const validPattern = /^[가-힣ㄱ-ㅎㅏ-ㅣa-zA-Z0-9_. ]*$/; + let error = ''; + + if (!validPattern.test(value)) { + error = + '13글자 이내의 한글, 영문, 숫자, 특수기호(_), (.)만 쓸 수 있어요.'; + } + + setEventNameError(error); + onErrorChange?.(error !== '' || participantCountError !== ''); + onEventNameChange(value); + }; + + const handleParticipantCountChange = (value: string) => { + if (value === '') { + setParticipantCountError(''); + onErrorChange?.(eventNameError !== ''); + onParticipantCountChange(value); + return; + } + + const sanitizedValue = value.replace(/\D/g, ''); + const numberValue = + sanitizedValue === '' ? '' : String(parseInt(sanitizedValue, 10)); + + const number = parseInt(numberValue, 10); + + let error = ''; + + if (numberValue === '' || isNaN(number)) { + error = ''; + onParticipantCountChange(''); + } else if (number === 0) { + error = '최소 1명 이상 가능해요'; + onParticipantCountChange(numberValue); + } else if (number > 64) { + error = '최대 64명까지 가능해요'; + onParticipantCountChange(numberValue); + } else { + error = ''; + onParticipantCountChange(numberValue); + } + + setParticipantCountError(error); + onErrorChange?.(error !== '' || eventNameError !== ''); + }; + + // 로컬 시간대 기준으로 어제 날짜를 YYYY-MM-DD로 계산 + const yesterdayDate = new Date(); + yesterdayDate.setDate(yesterdayDate.getDate() - 1); + const yyyy = yesterdayDate.getFullYear(); + const mm = String(yesterdayDate.getMonth() + 1).padStart(2, '0'); // 0-11이므로 +1 + const dd = String(yesterdayDate.getDate()).padStart(2, '0'); + const yesterday = `${yyyy}-${mm}-${dd}`; + + return ( +
+ + + +
+ ); +} diff --git a/src/feature/create-album/components/WaitingAlbum.tsx b/src/feature/create-album/components/WaitingAlbum.tsx new file mode 100644 index 00000000..9ff65690 --- /dev/null +++ b/src/feature/create-album/components/WaitingAlbum.tsx @@ -0,0 +1,98 @@ +'use client'; + +import CheeseCartLoading from '@/../public/assets/album/CheeseCart_Loading.json'; +import Toast from '@/global/components/toast/Toast'; +import { useImageStore } from '@/store/useImageStore'; +import { motion } from 'framer-motion'; +import dynamic from 'next/dynamic'; +import { useRouter } from 'next/navigation'; +import { useEffect } from 'react'; +const Lottie = dynamic(() => import('lottie-react'), { ssr: false }); + +interface WaitingAlbumProps { + albumId: string; +} + +export default function WaitingAlbum({ albumId }: WaitingAlbumProps) { + const router = useRouter(); + const { images } = useImageStore(); + + useEffect(() => { + const processImages = async () => { + const startTime = Date.now(); + + try { + // 최소 2.5초 대기 보장 + const elapsedTime = Date.now() - startTime; + const remainingTime = Math.max(0, 2500 - elapsedTime); + await new Promise((resolve) => setTimeout(resolve, remainingTime)); + // Zustand에 저장된 이미지가 있으면 → 일부 사진에 문제 → select로 이동 + if (images.length > 0) { + router.replace(`/album/${albumId}/select`); + return; + } + + // 업로드 완료 후 detail 페이지로 이동 + router.replace(`/album/detail/${albumId}`); + } catch (err) { + console.error('Image processing error:', err); + Toast.alert('사진 처리 중 에러가 발생했습니다.'); + router.replace(`/album/detail/${albumId}`); + } + }; + + processImages(); + }, [albumId, images, router]); + + const dotVariants = { + initial: { opacity: 0 }, + animate: { + opacity: 1, + transition: { + duration: 0.5, + repeat: Infinity, + repeatType: 'reverse' as const, + }, + }, + }; + + const containerVariants = { + animate: { + transition: { + staggerChildren: 0.3, + }, + }, + }; + + return ( +
+ +
+ 잠시만 기다려주세요 + + . + . + . + +
+
+ ); +} diff --git a/src/feature/create-album/hook/useCheckImages.ts b/src/feature/create-album/hook/useCheckImages.ts new file mode 100644 index 00000000..fc38d5ef --- /dev/null +++ b/src/feature/create-album/hook/useCheckImages.ts @@ -0,0 +1,21 @@ +import { useMutation, UseMutationOptions } from '@tanstack/react-query'; +import { checkImages, type CheckImagesResult } from '../utils/checkImages'; + +export type CheckImagesVariables = { + files: File[]; + albumId: string; +}; + +export function useCheckImages( + options?: UseMutationOptions< + CheckImagesResult, + unknown, + CheckImagesVariables + >, +) { + return useMutation({ + mutationKey: ['checkImages'], + mutationFn: ({ files, albumId }) => checkImages(files, albumId), + ...options, + }); +} diff --git a/src/feature/create-album/hook/useCreateAlbum.ts b/src/feature/create-album/hook/useCreateAlbum.ts new file mode 100644 index 00000000..352ea8f1 --- /dev/null +++ b/src/feature/create-album/hook/useCreateAlbum.ts @@ -0,0 +1,37 @@ +import { useMutation, UseMutationOptions } from '@tanstack/react-query'; +import { + createAlbumApi, + type CreateAlbumRequest, + type CreateAlbumResponse, +} from '../api/createAlbumApi'; + +export type CreateAlbumApiResponse = { + result: CreateAlbumResponse; + code: number; + isSuccess: boolean; + message: string; +}; + +export type CreateAlbumError = { + code: number; + isSuccess: boolean; + message: string; +}; + +export function useCreateAlbum( + options?: UseMutationOptions< + CreateAlbumApiResponse, + CreateAlbumError, + CreateAlbumRequest + >, +) { + return useMutation< + CreateAlbumApiResponse, + CreateAlbumError, + CreateAlbumRequest + >({ + mutationKey: ['createAlbum'], + mutationFn: createAlbumApi, + ...options, + }); +} diff --git a/src/feature/create-album/utils/checkImages.test.ts b/src/feature/create-album/utils/checkImages.test.ts new file mode 100644 index 00000000..ff51c0e6 --- /dev/null +++ b/src/feature/create-album/utils/checkImages.test.ts @@ -0,0 +1,43 @@ +import { checkAvailableCount } from '@/feature/create-album/api/checkAvailableCount'; +import { validateImages } from '@/feature/create-album/utils/validateImages'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { checkImages } from './checkImages'; + +// Mock dependencies +vi.mock('@/feature/create-album/api/checkAvailableCount', () => ({ + checkAvailableCount: vi.fn(), +})); + +vi.mock('@/feature/create-album/utils/validateImages', () => ({ + validateImages: vi.fn(), +})); + +describe('checkImages', () => { + const mockFiles = [new File([], 'test1.jpg')]; + const albumId = 'test-album-id'; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return oversized files and available count', async () => { + const mockOversizedFiles = ['oversized.jpg']; + const mockAvailableCount = 10; + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validateImages as any).mockReturnValue({ + oversizedFiles: mockOversizedFiles, + }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (checkAvailableCount as any).mockResolvedValue(mockAvailableCount); + + const result = await checkImages(mockFiles, albumId); + + expect(result).toEqual({ + oversizedFiles: mockOversizedFiles, + availableCount: mockAvailableCount, + }); + expect(validateImages).toHaveBeenCalledWith(mockFiles); + expect(checkAvailableCount).toHaveBeenCalledWith(albumId); + }); +}); diff --git a/src/feature/create-album/utils/checkImages.ts b/src/feature/create-album/utils/checkImages.ts new file mode 100644 index 00000000..e7f51fe8 --- /dev/null +++ b/src/feature/create-album/utils/checkImages.ts @@ -0,0 +1,26 @@ +import { checkAvailableCount } from '@/feature/create-album/api/checkAvailableCount'; +import { validateImages } from '@/feature/create-album/utils/validateImages'; + +export type CheckImagesResult = { + oversizedFiles: string[]; + availableCount: number; +}; + +/** + * 업로드 전 이미지 상태 확인 유틸 + * - oversizedFiles: 6MB를 초과한 파일 이름 목록 + * - availableCount: 서버에서 조회한 남은 업로드 가능 개수 + * 선택/표시가 필요한 화면(예: SelectAlbum)에서 사용하세요. + */ +export async function checkImages( + files: File[], + albumId: string, +): Promise { + const { oversizedFiles } = validateImages(files); + const availableCount = await checkAvailableCount(albumId); + + return { + oversizedFiles, + availableCount, + }; +} diff --git a/src/feature/create-album/utils/getFilesWithCaptureTime.ts b/src/feature/create-album/utils/getFilesWithCaptureTime.ts new file mode 100644 index 00000000..eec18412 --- /dev/null +++ b/src/feature/create-album/utils/getFilesWithCaptureTime.ts @@ -0,0 +1,32 @@ +import exifr from 'exifr'; + +export async function getFilesWithCaptureTime( + files: File[], +): Promise> { + return Promise.all( + files.map(async (file) => { + let date: Date = new Date(file.lastModified); + try { + const tags = await exifr.parse(file, { + pick: ['DateTimeOriginal', 'CreateDate', 'ModifyDate'], + translateValues: true, + }); + const dt: Date | undefined = + (tags?.DateTimeOriginal as Date | undefined) ?? + (tags?.CreateDate as Date | undefined) ?? + (tags?.ModifyDate as Date | undefined); + if ( + dt && + typeof dt.getTime === 'function' && + !Number.isNaN(dt.getTime()) + ) { + date = dt; + } + } catch {} + // yyyy-MM-ddTHH:mm:ss 형식으로 변환 + const pad = (n: number) => n.toString().padStart(2, '0'); + const captureTime = `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}T${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`; + return { file, captureTime }; + }), + ); +} diff --git a/src/feature/create-album/utils/handleFileUpload.test.ts b/src/feature/create-album/utils/handleFileUpload.test.ts new file mode 100644 index 00000000..ff5d1f0a --- /dev/null +++ b/src/feature/create-album/utils/handleFileUpload.test.ts @@ -0,0 +1,114 @@ +import { presignedAndUploadToNCP } from '@/global/api/presignedAndUploadToNCP'; +import { useUploadingStore } from '@/store/useUploadingStore'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { getFilesWithCaptureTime } from './getFilesWithCaptureTime'; +import { handleFileUpload } from './handleFileUpload'; +import { convertHeicFilesToJpeg } from './heicToJpeg'; +import { saveFilesToStore } from './saveFilesToStore'; +import { sortImagesByDate } from './sortImagesByDate'; +import { validateUpload } from './validateUpload'; + +// Mock dependencies +vi.mock('@/global/api/presignedAndUploadToNCP', () => ({ + presignedAndUploadToNCP: vi.fn(), +})); + +vi.mock('@/store/useUploadingStore', () => ({ + useUploadingStore: { + getState: vi.fn().mockReturnValue({ + setUploaded: vi.fn(), + setUploadedCount: vi.fn(), + }), + }, +})); + +vi.mock('./getFilesWithCaptureTime', () => ({ + getFilesWithCaptureTime: vi.fn(), +})); + +vi.mock('./heicToJpeg', () => ({ + convertHeicFilesToJpeg: vi.fn(), +})); + +vi.mock('./saveFilesToStore', () => ({ + saveFilesToStore: vi.fn(), +})); + +vi.mock('./sortImagesByDate', () => ({ + sortImagesByDate: vi.fn(), +})); + +vi.mock('./validateUpload', () => ({ + validateUpload: vi.fn(), +})); + +describe('handleFileUpload', () => { + const mockRouter = { push: vi.fn(), replace: vi.fn() }; + const albumId = 'test-album-id'; + const mockFile = new File(['content'], 'test.jpg', { type: 'image/jpeg' }); + const mockEvent = { + target: { + files: [mockFile], + value: 'some-value', + }, + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } as any; + + beforeEach(() => { + vi.clearAllMocks(); + // Default mock implementations + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (sortImagesByDate as any).mockResolvedValue([mockFile]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (convertHeicFilesToJpeg as any).mockResolvedValue([mockFile]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validateUpload as any).mockResolvedValue({ ok: true }); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (getFilesWithCaptureTime as any).mockResolvedValue([ + { file: mockFile, captureTime: '2023-01-01' }, + ]); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (presignedAndUploadToNCP as any).mockResolvedValue({ + success: 1, + failed: 0, + }); + }); + + it('should orchestrate the upload flow correctly', async () => { + await handleFileUpload(mockEvent, albumId, mockRouter); + + expect(sortImagesByDate).toHaveBeenCalled(); + expect(convertHeicFilesToJpeg).toHaveBeenCalled(); + expect(validateUpload).toHaveBeenCalled(); + expect(useUploadingStore.getState().setUploaded).toHaveBeenCalledWith(true); + expect(mockRouter.push).toHaveBeenCalledWith(`/album/${albumId}/waiting`); + expect(getFilesWithCaptureTime).toHaveBeenCalled(); + expect(presignedAndUploadToNCP).toHaveBeenCalled(); + expect(useUploadingStore.getState().setUploadedCount).toHaveBeenCalledWith( + 1, + ); + }); + + it('should handle validation failure', async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validateUpload as any).mockResolvedValue({ ok: false, reason: 'size' }); + + await handleFileUpload(mockEvent, albumId, mockRouter); + + expect(saveFilesToStore).toHaveBeenCalled(); + expect(mockRouter.push).toHaveBeenCalledWith(`/album/${albumId}/waiting`); + expect(presignedAndUploadToNCP).not.toHaveBeenCalled(); + }); + + it('should not redirect if stay option is true', async () => { + await handleFileUpload(mockEvent, albumId, mockRouter, { stay: true }); + + expect(mockRouter.push).not.toHaveBeenCalled(); + }); + + it('should clear input value after execution', async () => { + await handleFileUpload(mockEvent, albumId, mockRouter); + + expect(mockEvent.target.value).toBe(''); + }); +}); diff --git a/src/feature/create-album/utils/handleFileUpload.ts b/src/feature/create-album/utils/handleFileUpload.ts new file mode 100644 index 00000000..1e453380 --- /dev/null +++ b/src/feature/create-album/utils/handleFileUpload.ts @@ -0,0 +1,65 @@ +import { presignedAndUploadToNCP } from '@/global/api/presignedAndUploadToNCP'; +import { useUploadingStore } from '@/store/useUploadingStore'; +import { ChangeEvent } from 'react'; +import { getFilesWithCaptureTime } from './getFilesWithCaptureTime'; +import { convertHeicFilesToJpeg } from './heicToJpeg'; +import { saveFilesToStore } from './saveFilesToStore'; +import { sortImagesByDate } from './sortImagesByDate'; +import { validateUpload } from './validateUpload'; + +export async function handleFileUpload( + e: ChangeEvent, + albumId: string, + router?: { push: (path: string) => void; replace: (path: string) => void }, + options?: { stay?: boolean }, +): Promise<{ + success?: number; + failed?: number; + failedPhotoIds?: number[]; +}> { + const fl = e.target.files; + if (!fl) return {}; + + const startTime = Date.now(); + let uploadResult: { success: number; failed: number } | null = null; + + try { + let files = Array.from(fl).filter((f) => f.type.startsWith('image/')); + files = await sortImagesByDate(files); + files = await convertHeicFilesToJpeg(files); + + const validationResult = await validateUpload(files, albumId); + if (validationResult.ok) { + useUploadingStore.getState().setUploaded(true); + if (!options?.stay && router) { + router.push(`/album/${albumId}/waiting`); + } + const filesWithCapture = await getFilesWithCaptureTime(files); + const fileInfos = filesWithCapture.map(({ file, captureTime }) => ({ + fileName: file.name, + fileSize: file.size, + contentType: file.type, + captureTime, + })); + uploadResult = await presignedAndUploadToNCP({ + albumCode: albumId, + files, + fileInfos, + }); + + if (uploadResult.success > 0) { + useUploadingStore.getState().setUploadedCount(uploadResult.success); + } + } else { + saveFilesToStore(files); + if (router) { + router.push(`/album/${albumId}/waiting`); + } + return {}; + } + } finally { + if (e.target) e.target.value = ''; + } + + return uploadResult || {}; +} diff --git a/src/feature/create-album/utils/heicToJpeg.test.ts b/src/feature/create-album/utils/heicToJpeg.test.ts new file mode 100644 index 00000000..3ad1fa07 --- /dev/null +++ b/src/feature/create-album/utils/heicToJpeg.test.ts @@ -0,0 +1,75 @@ +import Toast from '@/global/components/toast/Toast'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { convertHeicFilesToJpeg } from './heicToJpeg'; + +// Mock dependencies +vi.mock('@/global/components/toast/Toast', () => ({ + default: { + alert: vi.fn(), + }, +})); + +vi.mock('heic-to', () => ({ + heicTo: vi.fn().mockImplementation(async ({ blob }) => { + // Return a dummy blob as if it were a JPEG + return new Blob(['dummy-jpeg-content'], { type: 'image/jpeg' }); + }), +})); + +describe('convertHeicFilesToJpeg', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return original files if no HEIC files are present', async () => { + const files = [ + new File(['content'], 'test1.jpg', { type: 'image/jpeg' }), + new File(['content'], 'test2.png', { type: 'image/png' }), + ]; + + const result = await convertHeicFilesToJpeg(files); + + expect(result).toHaveLength(2); + expect(result[0].name).toBe('test1.jpg'); + expect(result[1].name).toBe('test2.png'); + expect(Toast.alert).not.toHaveBeenCalled(); + }); + + it('should convert HEIC files to JPEG', async () => { + const files = [ + new File(['heic-content'], 'image.heic', { type: 'image/heic' }), + new File(['jpg-content'], 'image.jpg', { type: 'image/jpeg' }), + ]; + + const result = await convertHeicFilesToJpeg(files); + + expect(result).toHaveLength(2); + // The first file should be converted to jpg + expect(result[0].name).toBe('image.jpg'); + expect(result[0].type).toBe('image/jpeg'); + // The second file should remain as is + expect(result[1].name).toBe('image.jpg'); + expect(Toast.alert).toHaveBeenCalledWith( + '1개의 HEIC 파일을 변환 중입니다...', + ); + }); + + it('should handle conversion failure gracefully', async () => { + const { heicTo } = await import('heic-to'); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (heicTo as any).mockRejectedValueOnce(new Error('Conversion failed')); + + const files = [ + new File(['heic-content'], 'fail.heic', { type: 'image/heic' }), + ]; + + const result = await convertHeicFilesToJpeg(files); + + expect(result).toHaveLength(1); + // Should return original file on failure + expect(result[0].name).toBe('fail.heic'); + expect(Toast.alert).toHaveBeenCalledWith( + 'fail.heic 파일의 변환에 실패했습니다.', + ); + }); +}); diff --git a/src/feature/create-album/utils/heicToJpeg.ts b/src/feature/create-album/utils/heicToJpeg.ts new file mode 100644 index 00000000..2f729977 --- /dev/null +++ b/src/feature/create-album/utils/heicToJpeg.ts @@ -0,0 +1,41 @@ +import Toast from '@/global/components/toast/Toast'; +import { heicTo } from 'heic-to'; + +export async function convertHeicFilesToJpeg(files: File[]): Promise { + const heicFiles = files.filter( + (file) => + /heic|heif/i.test(file.type) || /\.heic$|\.heif$/i.test(file.name), + ); + + if (heicFiles.length > 0) { + Toast.alert(`${heicFiles.length}개의 HEIC 파일을 변환 중입니다...`); + } + + const convertedFiles = await Promise.all( + files.map(async (file) => { + if (/heic|heif/i.test(file.type) || /\.heic$|\.heif$/i.test(file.name)) { + try { + const jpegBlob = await heicTo({ + blob: file, + type: 'image/jpeg', + quality: 0.9, + }); + return new File( + [jpegBlob], + file.name.replace(/\.(heic|heif)$/i, '.jpg'), + { + type: 'image/jpeg', + lastModified: file.lastModified, + }, + ); + } catch (e) { + Toast.alert(`${file.name} 파일의 변환에 실패했습니다.`); + return file; + } + } + return file; + }), + ); + + return convertedFiles; +} diff --git a/src/feature/create-album/utils/saveFilesToStore.ts b/src/feature/create-album/utils/saveFilesToStore.ts new file mode 100644 index 00000000..5cbe3c69 --- /dev/null +++ b/src/feature/create-album/utils/saveFilesToStore.ts @@ -0,0 +1,15 @@ +import { useImageStore } from '@/store/useImageStore'; + +/** + * 이미지 파일 배열을 zustand 이미지 스토어에 저장 + * @param files File[] + */ +export function saveFilesToStore(files: File[]) { + const setImages = useImageStore.getState().setImages; + setImages( + files.map((file) => ({ + id: `${file.name}-${crypto.randomUUID()}`, + file, + })), + ); +} diff --git a/src/feature/create-album/utils/sortImagesByDate.ts b/src/feature/create-album/utils/sortImagesByDate.ts new file mode 100644 index 00000000..91258d30 --- /dev/null +++ b/src/feature/create-album/utils/sortImagesByDate.ts @@ -0,0 +1,11 @@ +import { getFilesWithCaptureTime } from './getFilesWithCaptureTime'; + +// captureTime(yyyy-MM-ddTHH:mm:ss) 기준 최신순 내림차순 정렬 +export async function sortImagesByDate(files: File[]): Promise { + const filesWithCapture = await getFilesWithCaptureTime(files); + filesWithCapture.sort( + (a, b) => + new Date(b.captureTime).getTime() - new Date(a.captureTime).getTime(), + ); + return filesWithCapture.map((f) => f.file); +} diff --git a/src/feature/create-album/utils/validateImages.ts b/src/feature/create-album/utils/validateImages.ts new file mode 100644 index 00000000..ce6ae113 --- /dev/null +++ b/src/feature/create-album/utils/validateImages.ts @@ -0,0 +1,27 @@ +const MAX_SIZE = 6 * 1024 * 1024; // 6MB in bytes + +export function validateImage(file: File): boolean { + return file.size <= MAX_SIZE; +} + +export function validateImageCount(files: File[]): number { + return files.filter((file) => !validateImage(file)).length; +} + +export function validateImages(files: File[]): { + valid: boolean; + oversizedFiles: string[]; +} { + const oversizedFiles: string[] = []; + + files.forEach((file) => { + if (!validateImage(file)) { + oversizedFiles.push(file.name); + } + }); + + return { + valid: oversizedFiles.length === 0, + oversizedFiles, + }; +} diff --git a/src/feature/create-album/utils/validateUpload.test.ts b/src/feature/create-album/utils/validateUpload.test.ts new file mode 100644 index 00000000..b7fc9567 --- /dev/null +++ b/src/feature/create-album/utils/validateUpload.test.ts @@ -0,0 +1,74 @@ +import { checkAvailableCount } from '@/feature/create-album/api/checkAvailableCount'; +import { validateImageCount } from '@/feature/create-album/utils/validateImages'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { validateUpload } from './validateUpload'; + +// Mock dependencies +vi.mock('@/feature/create-album/api/checkAvailableCount', () => ({ + checkAvailableCount: vi.fn(), +})); + +vi.mock('@/feature/create-album/utils/validateImages', () => ({ + validateImageCount: vi.fn(), +})); + +describe('validateUpload', () => { + const mockFiles = [new File([], 'test1.jpg'), new File([], 'test2.jpg')]; + const albumId = 'test-album-id'; + + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should return ok: true when validation passes', async () => { + // Mock validateImageCount to return 0 (no oversized files) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validateImageCount as any).mockReturnValue(0); + // Mock checkAvailableCount to return enough space + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (checkAvailableCount as any).mockResolvedValue(5); + + const result = await validateUpload(mockFiles, albumId); + + expect(result).toEqual({ ok: true }); + }); + + it('should return ok: false with reason: size when files are oversized', async () => { + // Mock validateImageCount to return 1 (1 oversized file) + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validateImageCount as any).mockReturnValue(1); + // Mock checkAvailableCount to return enough space + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (checkAvailableCount as any).mockResolvedValue(5); + + const result = await validateUpload(mockFiles, albumId); + + expect(result).toEqual({ ok: false, reason: 'size' }); + }); + + it('should return ok: false with reason: count when file count exceeds limit', async () => { + // Mock validateImageCount to return 0 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validateImageCount as any).mockReturnValue(0); + // Mock checkAvailableCount to return less than file count + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (checkAvailableCount as any).mockResolvedValue(1); + + const result = await validateUpload(mockFiles, albumId); + + expect(result).toEqual({ ok: false, reason: 'count' }); + }); + + it('should return ok: false with reason: both when both checks fail', async () => { + // Mock validateImageCount to return 1 + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (validateImageCount as any).mockReturnValue(1); + // Mock checkAvailableCount to return less than file count + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (checkAvailableCount as any).mockResolvedValue(1); + + const result = await validateUpload(mockFiles, albumId); + + expect(result).toEqual({ ok: false, reason: 'both' }); + }); +}); diff --git a/src/feature/create-album/utils/validateUpload.ts b/src/feature/create-album/utils/validateUpload.ts new file mode 100644 index 00000000..b3d52040 --- /dev/null +++ b/src/feature/create-album/utils/validateUpload.ts @@ -0,0 +1,36 @@ +import { checkAvailableCount } from '@/feature/create-album/api/checkAvailableCount'; +import { validateImageCount } from '@/feature/create-album/utils/validateImages'; + +export type ValidateUploadResult = + | { ok: true } + | { ok: false; reason: 'size' | 'count' | 'both' }; + +/** + * 이미지 업로드 전 검증 (용량, 업로드 가능 개수) + * WaitingAlbum에서 사용 - 성공/실패 reason만 반환 + * 상세 정보가 필요하면 SelectAlbumBody에서 validateImages 직접 사용 + */ +export async function validateUpload( + files: File[], + albumId: string, +): Promise { + const oversizedCount = validateImageCount(files); + const sizeValid = oversizedCount === 0; + + const availableCount = await checkAvailableCount(albumId); + const countValid = files.length <= availableCount; + + if (!sizeValid && !countValid) { + return { ok: false, reason: 'both' }; + } + + if (!sizeValid) { + return { ok: false, reason: 'size' }; + } + + if (!countValid) { + return { ok: false, reason: 'count' }; + } + + return { ok: true }; +} diff --git a/src/feature/login/components/KakaoSignupButton.tsx b/src/feature/login/components/KakaoSignupButton.tsx new file mode 100644 index 00000000..71337fdc --- /dev/null +++ b/src/feature/login/components/KakaoSignupButton.tsx @@ -0,0 +1,37 @@ +'use client'; +import { buildQuery } from '@/global/utils/buildQuery'; +import Image from 'next/image'; +import { useSearchParams } from 'next/navigation'; + +const KAKAO_AUTH_URL = `https://dev.say-cheese.me/oauth2/authorization/kakao`; + +export default function KakaoSignupButton() { + const searchParams = useSearchParams(); + const redirect = searchParams.get('redirect'); + + const handleKakaoLogin = async () => { + try { + const kakaoUrl = redirect + ? `${KAKAO_AUTH_URL}${buildQuery({ redirect })}` + : KAKAO_AUTH_URL; + + window.location.href = kakaoUrl; + } catch (err) { + console.error('카카오 인증 GET 요청 실패:', err); + } + }; + return ( +
+ 카카오 로고 + 카카오 로그인 +
+ ); +} diff --git a/src/feature/main/closed-album/components/ClosedAlbumSectionList.tsx b/src/feature/main/closed-album/components/ClosedAlbumSectionList.tsx new file mode 100644 index 00000000..a794d397 --- /dev/null +++ b/src/feature/main/closed-album/components/ClosedAlbumSectionList.tsx @@ -0,0 +1,30 @@ +import CloseAlbum from '@/feature/main/components/close-album/CloseAlbum'; +import { type ClosedAlbumSection } from '../utils/buildClosedAlbumSections'; + +interface ClosedAlbumSectionListProps { + sections: ClosedAlbumSection[]; +} + +export default function ClosedAlbumSectionList({ + sections, +}: ClosedAlbumSectionListProps) { + if (sections.length === 0) return null; + + return sections.map(({ year, albums }) => ( +
+

{year}

+
+ {albums.map((album) => ( + + ))} +
+
+ )); +} diff --git a/src/feature/main/closed-album/components/ScreenMainClosedAlbum.tsx b/src/feature/main/closed-album/components/ScreenMainClosedAlbum.tsx new file mode 100644 index 00000000..e6255888 --- /dev/null +++ b/src/feature/main/closed-album/components/ScreenMainClosedAlbum.tsx @@ -0,0 +1,69 @@ +'use client'; +import EmptyAlbum from '@/feature/main/components/EmptyAlbum'; +import { useAlbumClosedInfiniteQuery } from '@/feature/main/hooks/useAlbumClosedInfiniteQuery'; +import CustomHeader from '@/global/components/header/CustomHeader'; +import { useEffect, useMemo, useRef } from 'react'; +import { buildClosedAlbumSections } from '../utils/buildClosedAlbumSections'; +import ClosedAlbumSectionList from './ClosedAlbumSectionList'; + +const LOADING_TEXT = '불러오는 중...'; + +interface ScreenMainClosedAlbumProps {} + +export default function ScreenMainClosedAlbum({}: ScreenMainClosedAlbumProps) { + const { items, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = + useAlbumClosedInfiniteQuery(); + const loadMoreRef = useRef(null); + + const sections = useMemo(() => buildClosedAlbumSections(items), [items]); + const showLoadingState = isLoading && items.length === 0; + const showEmptyState = !isLoading && items.length === 0; + + useEffect(() => { + if (!hasNextPage) return; + const target = loadMoreRef.current; + if (!target) return; + + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, + { rootMargin: '200px 0px' }, + ); + + observer.observe(target); + + return () => { + observer.disconnect(); + }; + }, [fetchNextPage, hasNextPage, isFetchingNextPage]); + + return ( +
+ +
+ {showLoadingState && ( +
+ {LOADING_TEXT} +
+ )} + + {!showLoadingState && showEmptyState && ( + + )} + + {!showLoadingState && } + + {isFetchingNextPage && ( +
+ {LOADING_TEXT} +
+ )} +
+
+
+ ); +} diff --git a/src/feature/main/closed-album/utils/buildClosedAlbumSections.ts b/src/feature/main/closed-album/utils/buildClosedAlbumSections.ts new file mode 100644 index 00000000..ee62553e --- /dev/null +++ b/src/feature/main/closed-album/utils/buildClosedAlbumSections.ts @@ -0,0 +1,78 @@ +import { type AlbumClosedItem } from '@/feature/main/hooks/useAlbumClosedInfiniteQuery'; +import { formatEventDate } from '@/global/utils/formatEventDate'; + +const MAX_IMAGES = 4; + +export interface ClosedAlbumSectionItem { + code: string; + title: string; + date: string; + author: string; + images: string[]; + rawDate: string; +} + +export interface ClosedAlbumSection { + year: string; + albums: ClosedAlbumSectionItem[]; +} + +export function buildClosedAlbumSections( + items: AlbumClosedItem[], +): ClosedAlbumSection[] { + const grouped = items.reduce>( + (acc, item) => { + const year = extractYear(item.eventDate); + if (!acc[year]) { + acc[year] = []; + } + + acc[year].push({ + code: item.code, + title: item.title ?? '', + date: formatEventDate(item.eventDate), + author: item.makerName ?? '', + images: + item.thumbnails + ?.filter((thumbnail): thumbnail is string => Boolean(thumbnail)) + ?.slice(0, MAX_IMAGES) ?? [], + rawDate: item.eventDate ?? '', + }); + + return acc; + }, + {}, + ); + + return Object.entries(grouped) + .sort(([a], [b]) => toComparableYear(b) - toComparableYear(a)) + .map(([year, albums]) => ({ + year, + albums: albums.sort((a, b) => compareByDateDesc(a.rawDate, b.rawDate)), + })); +} + +function toComparableYear(year: string) { + const yearNumber = Number(year); + return Number.isNaN(yearNumber) ? 0 : yearNumber; +} + +function compareByDateDesc(a: string, b: string) { + const aTime = Date.parse(a); + const bTime = Date.parse(b); + if (Number.isNaN(aTime) || Number.isNaN(bTime)) { + return 0; + } + + return bTime - aTime; +} + +function extractYear(date?: string) { + if (!date) return '기타'; + const parsed = new Date(date); + if (Number.isNaN(parsed.getTime())) { + return date.slice(0, 4) || '기타'; + } + + return String(parsed.getFullYear()); +} diff --git a/src/feature/main/components/EmptyAlbum.tsx b/src/feature/main/components/EmptyAlbum.tsx new file mode 100644 index 00000000..10ef93fc --- /dev/null +++ b/src/feature/main/components/EmptyAlbum.tsx @@ -0,0 +1,16 @@ +import EmptySvg from './svg/EmptySvg'; + +interface EmptyAlbumProps { + title: string; +} + +export default function EmptyAlbum({ title }: EmptyAlbumProps) { + return ( +
+ +

+ {title} +

+
+ ); +} diff --git a/src/feature/main/components/ScreenMain.tsx b/src/feature/main/components/ScreenMain.tsx new file mode 100644 index 00000000..645ba45f --- /dev/null +++ b/src/feature/main/components/ScreenMain.tsx @@ -0,0 +1,28 @@ +'use client'; +import LogoHeader from '@/global/components/header/LogoHeader'; +import LongButton from '@/global/components/LongButton'; +import { useRouter } from 'next/navigation'; +import CloseAlbumContainer from './close-album/CloseAlbumContainer'; +import OpenAlbumContainer from './open-album/OpenAlbumContainer'; +import ProfileMypage from './profile/ProfileMypage'; + +interface ScreenMainProps {} + +export default function ScreenMain({}: ScreenMainProps) { + const router = useRouter(); + + return ( +
+ + + + +
+ router.push('/create-album')} + /> +
+
+ ); +} diff --git a/src/feature/main/components/close-album/CloseAlbum.tsx b/src/feature/main/components/close-album/CloseAlbum.tsx new file mode 100644 index 00000000..4d486415 --- /dev/null +++ b/src/feature/main/components/close-album/CloseAlbum.tsx @@ -0,0 +1,53 @@ +import Link from 'next/link'; +import SvgCloseAlbumEmptyPhoto from '../svg/SvgCloseAlbumEmptyPhoto'; + +interface CloseAlbumProps { + code: string; + title: string; + date: string; + author: string; + images: string[]; +} + +export default function CloseAlbum({ + code, + title, + date, + author, + images, +}: CloseAlbumProps) { + return ( + +
+ {images.length < 4 ? ( +
+ +
+ ) : ( +
+ {images.slice(0, 4).map((src, i) => ( + {`이미지${i + ))} +
+ )} +
+ +
+

+ {title} +

+
+ {date} · {author} +
+
+ + ); +} diff --git a/src/feature/main/components/close-album/CloseAlbumContainer.tsx b/src/feature/main/components/close-album/CloseAlbumContainer.tsx new file mode 100644 index 00000000..f6c256d8 --- /dev/null +++ b/src/feature/main/components/close-album/CloseAlbumContainer.tsx @@ -0,0 +1,93 @@ +'use client'; +import { ChevronRight } from 'lucide-react'; +import Link from 'next/link'; +import { useEffect, useMemo, useRef } from 'react'; +import { useAlbumClosedInfiniteQuery } from '../../hooks/useAlbumClosedInfiniteQuery'; +import { mapClosedAlbumItems } from '../../utils/mapClosedAlbumItems'; +import EmptyAlbum from '../EmptyAlbum'; +import CloseAlbum from './CloseAlbum'; + +const LOADING_TEXT = '불러오는 중...'; + +export default function CloseAlbumContainer() { + const { items, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = + useAlbumClosedInfiniteQuery(); + const loadMoreRef = useRef(null); + const albums = useMemo(() => mapClosedAlbumItems(items), [items]); + const showLoadingState = isLoading && albums.length === 0; + const showEmptyState = !isLoading && albums.length === 0; + + useEffect(() => { + if (!hasNextPage) return; + const target = loadMoreRef.current; + if (!target) return; + + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, + { + rootMargin: '200px 0px', + }, + ); + + observer.observe(target); + + return () => { + observer.disconnect(); + }; + }, [fetchNextPage, hasNextPage, isFetchingNextPage]); + + return ( +
+ {albums.length > 0 ? ( +
+ +

+ 닫힌 앨범 {albums.length} + +

+ +
+ ) : ( +

+ 닫힌 앨범 0 +

+ )} +
+ {showLoadingState && ( +
+ {LOADING_TEXT} +
+ )} + {!showLoadingState && + albums.map((album) => ( + + ))} + {!showLoadingState && showEmptyState && ( + + )} +
+ {isFetchingNextPage && ( +
+ {LOADING_TEXT} +
+ )} +
+
+ ); +} diff --git a/src/feature/main/components/open-album/ButtonMore.tsx b/src/feature/main/components/open-album/ButtonMore.tsx new file mode 100644 index 00000000..592a88d3 --- /dev/null +++ b/src/feature/main/components/open-album/ButtonMore.tsx @@ -0,0 +1,25 @@ +import { ChevronDown } from 'lucide-react'; + +interface ButtonMoreProps { + onClick?: () => void; + moreCount: number; +} + +export default function ButtonMore({ onClick, moreCount }: ButtonMoreProps) { + const handleClick = () => { + onClick?.(); + }; + + return ( + + ); +} diff --git a/src/feature/main/components/open-album/OpenAlbum.tsx b/src/feature/main/components/open-album/OpenAlbum.tsx new file mode 100644 index 00000000..64f09599 --- /dev/null +++ b/src/feature/main/components/open-album/OpenAlbum.tsx @@ -0,0 +1,146 @@ +import PersonSvg from '@/global/svg/PersonSvg'; +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; +import Image from 'next/image'; +import Link from 'next/link'; +import { getOpenAlbumGridConfig } from '../../utils/getOpenAlbumGridConfig'; + +const defaultThumbnail = '/assets/album/bg-album-default.png'; + +interface OpenAlbumProps { + code: string; + expirationTime: string; + title: string; + date: string; + author: string; + totalMembers: number; + joinedMembers: number; + thumbnails?: string[]; + emoji: string; +} + +export default function OpenAlbum({ + code, + expirationTime, + title, + date, + author, + totalMembers, + joinedMembers, + thumbnails = [], + emoji, +}: OpenAlbumProps) { + const [main, side1, side2] = thumbnails; + const count = thumbnails.length; + const gridConfig = getOpenAlbumGridConfig(count); + + return ( + +
+
+
+
+ {/* 메인 썸네일 */} +
+ {main ? ( + 메인 사진 + ) : ( + 메인 사진 + )} +
+ + {/* 썸네일 2개 이상일 때 side1 */} + {count >= 2 && ( +
+ {side1 && ( + 주요 사진 + )} +
+ )} + + {/* 썸네일 3개일 때만 side2 */} + {count >= 3 && ( +
+ {side2 && ( + 주요 사진 + )} +
+ )} +
+
+ + {/* 소멸까지 */} +
+ + 소멸까지 {expirationTime} + +
+
+ + {/* 아래 정보 */} +
+
+ {emoji ? convertUnicodeToEmoji(emoji) : '😀'} +
+
+

+ {title} +

+

+ {`${date} · ${author}`} +

+
+ +
+
+ + + {joinedMembers} / {totalMembers} 명 + +
+
+
+
+ + ); +} diff --git a/src/feature/main/components/open-album/OpenAlbumContainer.tsx b/src/feature/main/components/open-album/OpenAlbumContainer.tsx new file mode 100644 index 00000000..655aaafc --- /dev/null +++ b/src/feature/main/components/open-album/OpenAlbumContainer.tsx @@ -0,0 +1,121 @@ +'use client'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { + useAlbumOpenInfiniteQuery, + type AlbumOpenType, +} from '../../hooks/useAlbumOpenInfiniteQuery'; +import { mapOpenAlbumItems } from '../../utils/mapOpenAlbumItems'; +import EmptyAlbum from '../EmptyAlbum'; +import ButtonMore from './ButtonMore'; +import OpenAlbum from './OpenAlbum'; +import SkeletonOpenAlbum from './SkeletonOpenAlbum'; +import ToggleAlbumType from './ToggleAlbumType'; + +const MIN_VISIBLE_COUNT = 2; + +interface OpenAlbumContainerProps {} + +export default function OpenAlbumContainer({}: OpenAlbumContainerProps) { + const [albumType, setAlbumType] = useState('all'); + const [isMoreOpened, setIsMoreOpened] = useState(false); + const [hasOpenedMine, setHasOpenedMine] = useState(false); + const loadMoreRef = useRef(null); + + useEffect(() => { + if (albumType === 'mine' && !hasOpenedMine) { + setHasOpenedMine(true); + } + }, [albumType, hasOpenedMine]); + + useEffect(() => { + setIsMoreOpened(false); + }, [albumType]); + + const allQuery = useAlbumOpenInfiniteQuery({ type: 'all' }); + const mineQuery = useAlbumOpenInfiniteQuery({ + type: 'mine', + enabled: albumType === 'mine' || hasOpenedMine, + }); + + const activeQuery = albumType === 'all' ? allQuery : mineQuery; + const { + items: activeItems, + fetchNextPage, + hasNextPage, + isFetchingNextPage, + isLoading, + } = activeQuery; + + const albums = useMemo(() => mapOpenAlbumItems(activeItems), [activeItems]); + const showing = isMoreOpened ? albums : albums.slice(0, MIN_VISIBLE_COUNT); + const moreCount = Math.max(albums.length - MIN_VISIBLE_COUNT, 0); + const showMoreButton = !isMoreOpened && moreCount > 0; + const showLoadingState = isLoading && albums.length === 0; + const showEmptyState = !isLoading && albums.length === 0; + + useEffect(() => { + if (!isMoreOpened) return; + if (!hasNextPage) return; + + const target = loadMoreRef.current; + if (!target) return; + + const observer = new IntersectionObserver( + (entries) => { + const entry = entries[0]; + if (entry.isIntersecting && hasNextPage && !isFetchingNextPage) { + fetchNextPage(); + } + }, + { + rootMargin: '200px 0px', + }, + ); + + observer.observe(target); + + return () => { + observer.disconnect(); + }; + }, [fetchNextPage, hasNextPage, isFetchingNextPage, isMoreOpened]); + + return ( +
+

+ 열린 앨범 {albums.length} +

+ setAlbumType(next)} + /> +
+ {!showLoadingState && + showing.map((album) => )} + {showLoadingState && ( + <> + + + + )} + {!showLoadingState && showEmptyState && ( + + )} +
+ {showMoreButton && ( + setIsMoreOpened(true)} + /> + )} + +
+
+ ); +} diff --git a/src/feature/main/components/open-album/SkeletonOpenAlbum.tsx b/src/feature/main/components/open-album/SkeletonOpenAlbum.tsx new file mode 100644 index 00000000..5463f0cd --- /dev/null +++ b/src/feature/main/components/open-album/SkeletonOpenAlbum.tsx @@ -0,0 +1,7 @@ +interface SkeletonOpenAlbumProps {} + +export default function SkeletonOpenAlbum({}: SkeletonOpenAlbumProps) { + return ( +
+ ); +} diff --git a/src/feature/main/components/open-album/ToggleAlbumType.tsx b/src/feature/main/components/open-album/ToggleAlbumType.tsx new file mode 100644 index 00000000..d4f74ea4 --- /dev/null +++ b/src/feature/main/components/open-album/ToggleAlbumType.tsx @@ -0,0 +1,40 @@ +'use client'; + +interface ToggleAlbumTypeProps { + value: T; + onChange: (next: T) => void; + labels: Record; +} + +export default function ToggleAlbumType({ + value, + onChange, + labels, +}: ToggleAlbumTypeProps) { + const keys = Object.keys(labels) as T[]; + + return ( +
+ {keys.map((key) => { + const isActive = value === key; + const label = labels[key]; + + return ( + + ); + })} +
+ ); +} diff --git a/src/feature/main/components/profile/ProfileMypage.tsx b/src/feature/main/components/profile/ProfileMypage.tsx new file mode 100644 index 00000000..43db4294 --- /dev/null +++ b/src/feature/main/components/profile/ProfileMypage.tsx @@ -0,0 +1,72 @@ +'use client'; +import { DEFAULT_PROFILE_IMAGE } from '@/global/constants/images'; +import { Settings } from 'lucide-react'; +import Link from 'next/link'; +import { useRouter } from 'next/navigation'; +import { useGetUserMe } from '../../hooks/useGetUserMe'; + +interface ProfileMypageProps {} + +export default function ProfileMypage({}: ProfileMypageProps) { + const router = useRouter(); + const { data } = useGetUserMe(); + + return ( +
+
+
+ 프로필사진 +
+ +
+
+ + {data?.name ?? '사용자'} + + + + + +
+ +
+
+ + {data?.albumCount ?? 0} + + + 앨범 수 + +
+
+ + {data?.photoCount ?? 0} + + + 올린 사진 + +
+
+ + {data?.likesCount ?? 0} + + + 받은 띱 + +
+
+
+
+
+ ); +} diff --git a/src/feature/main/components/svg/EmptySvg.tsx b/src/feature/main/components/svg/EmptySvg.tsx new file mode 100644 index 00000000..83bb749f --- /dev/null +++ b/src/feature/main/components/svg/EmptySvg.tsx @@ -0,0 +1,35 @@ +interface EmptySvgProps {} + +export default function EmptySvg({}: EmptySvgProps) { + return ( + + + + + + ); +} diff --git a/src/feature/main/components/svg/SvgCloseAlbumEmptyPhoto.tsx b/src/feature/main/components/svg/SvgCloseAlbumEmptyPhoto.tsx new file mode 100644 index 00000000..3b0afc35 --- /dev/null +++ b/src/feature/main/components/svg/SvgCloseAlbumEmptyPhoto.tsx @@ -0,0 +1,26 @@ +interface SvgCloseAlbumEmptyPhotoProps {} + +export default function SvgCloseAlbumEmptyPhoto({}: SvgCloseAlbumEmptyPhotoProps) { + return ( + + + + + + + + + + + + ); +} diff --git a/src/feature/main/hooks/useAlbumClosedInfiniteQuery.ts b/src/feature/main/hooks/useAlbumClosedInfiniteQuery.ts new file mode 100644 index 00000000..2876f63c --- /dev/null +++ b/src/feature/main/hooks/useAlbumClosedInfiniteQuery.ts @@ -0,0 +1,65 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useInfiniteQuery } from '@tanstack/react-query'; + +type AlbumClosedPage = NonNullable; +export type AlbumClosedItem = AlbumClosedPage['responses'][number]; + +interface FetchPageParams { + pageParam: number; + /** 페이지 사이즈 (기본 20) */ + size: number; +} + +const fetchData = async ({ + pageParam, + size, +}: FetchPageParams): Promise => { + const res = await api.get({ + path: EP.album.albumClosed(), + params: { page: pageParam, size }, + }); + + const result: AlbumClosedPage = res.result ?? createEmptyPage({ pageParam }); + + return { ...result, page: pageParam }; +}; + +const createEmptyPage = ({ + pageParam, +}: { + pageParam: number; +}): AlbumClosedPage => ({ + responses: [], + listSize: 0, + isFirst: pageParam === 0, + isLast: true, + hasNext: false, +}); + +interface UseAlbumClosedInfiniteQueryProps { + size?: number; + enabled?: boolean; +} + +export function useAlbumClosedInfiniteQuery({ + size = 20, + enabled = true, +}: UseAlbumClosedInfiniteQueryProps = {}) { + const query = useInfiniteQuery({ + queryKey: [EP.album.albumClosed(), size], + initialPageParam: 0, + enabled: enabled, + queryFn: ({ pageParam }) => fetchData({ pageParam, size }), + getNextPageParam: (lastPage) => + lastPage.hasNext ? lastPage.page + 1 : undefined, + }); + + const items: AlbumClosedItem[] = + query.data?.pages.flatMap((p) => p.responses ?? []) ?? []; + + return { + ...query, + items, + }; +} diff --git a/src/feature/main/hooks/useAlbumOpenInfiniteQuery.ts b/src/feature/main/hooks/useAlbumOpenInfiniteQuery.ts new file mode 100644 index 00000000..99e14b17 --- /dev/null +++ b/src/feature/main/hooks/useAlbumOpenInfiniteQuery.ts @@ -0,0 +1,74 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useInfiniteQuery } from '@tanstack/react-query'; + +type AlbumOpenType = 'all' | 'mine'; +type AlbumOpenPage = NonNullable; +type AlbumOpenItem = AlbumOpenPage['responses'][number]; + +interface FetchPageParams { + pageParam: number; + /** 페이지 사이즈 (기본 20) */ + size: number; + path: string; +} + +const fetchData = async ({ + pageParam, + size, + path, +}: FetchPageParams): Promise => { + const res = await api.get({ + path, + params: { page: pageParam, size }, + }); + + const result: AlbumOpenPage = res.result ?? createEmptyPage({ pageParam }); + + return { ...result, page: pageParam }; +}; + +const createEmptyPage = ({ + pageParam, +}: { + pageParam: number; +}): AlbumOpenPage => ({ + responses: [], + listSize: 0, + isFirst: pageParam === 0, + isLast: true, + hasNext: false, +}); + +interface UseAlbumOpenInfiniteQueryProps { + size?: number; + enabled?: boolean; + type?: AlbumOpenType; +} + +export function useAlbumOpenInfiniteQuery({ + size = 10, + enabled = true, + type = 'all', +}: UseAlbumOpenInfiniteQueryProps = {}) { + const path = type === 'mine' ? EP.album.albumOpenMe() : EP.album.albumOpen(); + const query = useInfiniteQuery({ + queryKey: [path, size], + initialPageParam: 0, + enabled: enabled, + queryFn: ({ pageParam }) => fetchData({ pageParam, size, path }), + getNextPageParam: (lastPage) => + lastPage.hasNext ? lastPage.page + 1 : undefined, + }); + + const items: AlbumOpenItem[] = + query.data?.pages.flatMap((p) => p.responses ?? []) ?? []; + + return { + type, + ...query, + items, + }; +} + +export type { AlbumOpenItem, AlbumOpenType }; diff --git a/src/feature/main/hooks/useGetUserMe.ts b/src/feature/main/hooks/useGetUserMe.ts new file mode 100644 index 00000000..84e1d0ef --- /dev/null +++ b/src/feature/main/hooks/useGetUserMe.ts @@ -0,0 +1,26 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery, UseQueryOptions } from '@tanstack/react-query'; + +const fetchData = async () => { + const response = await api.get({ + path: EP.user.userMe(), + }); + + return response.result; +}; + +export function useGetUserMe( + options?: Omit< + UseQueryOptions, + 'queryKey' | 'queryFn' + >, +) { + const query = useQuery({ + queryKey: [EP.user.userMe()], + queryFn: () => fetchData(), + ...options, + }); + + return query; +} diff --git a/src/feature/main/utils/getOpenAlbumGridConfig.ts b/src/feature/main/utils/getOpenAlbumGridConfig.ts new file mode 100644 index 00000000..1178c4b7 --- /dev/null +++ b/src/feature/main/utils/getOpenAlbumGridConfig.ts @@ -0,0 +1,31 @@ +interface GetOpenAlbumGridConfig { + columns: string; + rows: string; + areas: string; + side1Row?: string; +} + +export function getOpenAlbumGridConfig(count: number): GetOpenAlbumGridConfig { + if (count <= 1) { + return { + columns: '1fr', + rows: '1fr', + areas: '"main"', + }; + } + + if (count === 2) { + return { + columns: '2fr 1fr', + rows: '1fr', + areas: '"main side1"', + side1Row: '1 / span 2', + }; + } + + return { + columns: '2fr 1fr', + rows: '1fr 1fr', + areas: '"main side1"\n "main side2"', + }; +} diff --git a/src/feature/main/utils/mapClosedAlbumItems.ts b/src/feature/main/utils/mapClosedAlbumItems.ts new file mode 100644 index 00000000..d4d1bffa --- /dev/null +++ b/src/feature/main/utils/mapClosedAlbumItems.ts @@ -0,0 +1,27 @@ +import { formatEventDate } from '@/global/utils/formatEventDate'; +import { AlbumClosedItem } from '../hooks/useAlbumClosedInfiniteQuery'; + +const MAX_IMAGES = 4; + +interface CloseAlbumListItem { + code: string; + title: string; + date: string; + author: string; + images: string[]; +} + +export function mapClosedAlbumItems( + items: AlbumClosedItem[], +): CloseAlbumListItem[] { + return items.map((item) => ({ + code: item.code, + title: item.title ?? '', + date: formatEventDate(item.eventDate), + author: item.makerName ?? '', + images: + item.thumbnails + ?.filter((thumbnail): thumbnail is string => Boolean(thumbnail)) + ?.slice(0, MAX_IMAGES) ?? [], + })); +} diff --git a/src/feature/main/utils/mapOpenAlbumItems.ts b/src/feature/main/utils/mapOpenAlbumItems.ts new file mode 100644 index 00000000..28d0cd14 --- /dev/null +++ b/src/feature/main/utils/mapOpenAlbumItems.ts @@ -0,0 +1,32 @@ +import { formatEventDate } from '@/global/utils/formatEventDate'; +import { formatExpirationTime } from '@/global/utils/time/formatExpirationTime'; +import { AlbumOpenItem } from '../hooks/useAlbumOpenInfiniteQuery'; + +interface OpenAlbumListItem { + code: string; + author: string; + date: string; + expirationTime: string; + joinedMembers: number; + totalMembers: number; + title: string; + thumbnails: string[]; + emoji: string; +} + +export function mapOpenAlbumItems(items: AlbumOpenItem[]): OpenAlbumListItem[] { + return items.map((item) => ({ + code: item.code, + author: item.makerName ?? '', + date: formatEventDate(item.eventDate), + expirationTime: formatExpirationTime(item.expiredAt), + joinedMembers: item.currentParticipant ?? 0, + totalMembers: item.participant ?? 0, + title: item.title ?? '', + thumbnails: + item.recentPhotoThumbnails + ?.slice(0, 3) + .filter((thumbnail): thumbnail is string => Boolean(thumbnail)) ?? [], + emoji: item.themeEmoji, + })); +} diff --git a/src/feature/mypage/components/ButtonDeleteAccount.tsx b/src/feature/mypage/components/ButtonDeleteAccount.tsx new file mode 100644 index 00000000..65ad66de --- /dev/null +++ b/src/feature/mypage/components/ButtonDeleteAccount.tsx @@ -0,0 +1,32 @@ +import ConfirmModal from '@/global/components/modal/ConfirmModal'; + +interface ButtonDeleteAccountProps { + onConfirm?: () => Promise | void; +} + +export default function ButtonDeleteAccount({ + onConfirm, +}: ButtonDeleteAccountProps) { + const handleConfirm = async () => { + try { + if (onConfirm) await onConfirm(); + } catch (err) { + console.error(err); + } + }; + + return ( + + 탈퇴하기 + + } + title='정말 탈퇴하시겠어요?' + description='계정은 삭제되며, 복구되지 않아요.' + cancelText='다음에' + confirmText='탈퇴하기' + onConfirm={handleConfirm} + /> + ); +} diff --git a/src/feature/mypage/components/ButtonLogout.tsx b/src/feature/mypage/components/ButtonLogout.tsx new file mode 100644 index 00000000..75f0f27a --- /dev/null +++ b/src/feature/mypage/components/ButtonLogout.tsx @@ -0,0 +1,33 @@ +import ConfirmModal from '@/global/components/modal/ConfirmModal'; +import { useRouter } from 'next/navigation'; +import { useLogoutMutation } from '../hooks/useLogoutMutation'; + +interface ButtonLogoutProps {} + +export default function ButtonLogout({}: ButtonLogoutProps) { + const router = useRouter(); + const { mutateAsync } = useLogoutMutation(); + + const handleConfirm = async () => { + try { + await mutateAsync(); + router.push('/'); + } catch (err) { + console.error(err); + } + }; + + return ( + + 로그아웃 + + } + title='로그아웃 하시겠어요?' + cancelText='다음에' + confirmText='로그아웃' + onConfirm={handleConfirm} + /> + ); +} diff --git a/src/feature/mypage/components/ModalLoginExpired.tsx b/src/feature/mypage/components/ModalLoginExpired.tsx new file mode 100644 index 00000000..e69de29b diff --git a/src/feature/mypage/components/ProfileSetting.tsx b/src/feature/mypage/components/ProfileSetting.tsx new file mode 100644 index 00000000..21e1c94b --- /dev/null +++ b/src/feature/mypage/components/ProfileSetting.tsx @@ -0,0 +1,36 @@ +'use client'; +import { useGetUserMe } from '@/feature/main/hooks/useGetUserMe'; +import { DEFAULT_PROFILE_IMAGE } from '@/global/constants/images'; + +interface ProfileSettingProps {} + +export default function ProfileSetting({}: ProfileSettingProps) { + const { data } = useGetUserMe(); + + return ( +
+
+
+
+ 프로필 이미지 +
+ +
+

+ {data?.name} +

+

+ {data?.email} +

+
+
+
+
+ ); +} diff --git a/src/feature/mypage/components/ScreenMypage.tsx b/src/feature/mypage/components/ScreenMypage.tsx new file mode 100644 index 00000000..d63bb529 --- /dev/null +++ b/src/feature/mypage/components/ScreenMypage.tsx @@ -0,0 +1,15 @@ +import CustomHeader from '@/global/components/header/CustomHeader'; +import ProfileSetting from './ProfileSetting'; +import SettingButtons from './SettingButtons'; + +interface ScreenMypageProps {} + +export default function ScreenMypage({}: ScreenMypageProps) { + return ( + <> + + + + + ); +} diff --git a/src/feature/mypage/components/SettingButtons.tsx b/src/feature/mypage/components/SettingButtons.tsx new file mode 100644 index 00000000..be92f591 --- /dev/null +++ b/src/feature/mypage/components/SettingButtons.tsx @@ -0,0 +1,22 @@ +'use client'; +import Link from 'next/link'; +import ButtonLogout from './ButtonLogout'; + +interface SettingButtonsProps {} + +export default function SettingButtons({}: SettingButtonsProps) { + return ( +
+ + 서비스 이용약관 + + + 개인정보 처리방침 + + + + {/* TODO : 기능 만들기 전까지 주석처리 */} + {/* */} +
+ ); +} diff --git a/src/feature/mypage/hooks/useLogoutMutation.ts b/src/feature/mypage/hooks/useLogoutMutation.ts new file mode 100644 index 00000000..abc98dc7 --- /dev/null +++ b/src/feature/mypage/hooks/useLogoutMutation.ts @@ -0,0 +1,14 @@ +import { EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +const fetchData = async () => { + const res = await api.post({ path: EP.auth.logout() }); + return res.result; +}; + +export function useLogoutMutation() { + const mutation = useMutation({ mutationFn: () => fetchData() }); + + return mutation; +} diff --git a/src/feature/onboarding/components/ProfileAgree.tsx b/src/feature/onboarding/components/ProfileAgree.tsx new file mode 100644 index 00000000..ef793e1b --- /dev/null +++ b/src/feature/onboarding/components/ProfileAgree.tsx @@ -0,0 +1,107 @@ +'use client'; +import { Check, ChevronRight } from 'lucide-react'; +import Link from 'next/link'; + +interface AgreementItem { + id: string; + label: string; + required: boolean; +} + +const agreementItems: AgreementItem[] = [ + { id: 'terms', label: '[필수] 이용약관 동의', required: true }, + { id: 'privacy', label: '[필수] 개인정보 수집 및 이용 동의', required: true }, + { + id: 'thirdParty', + label: '[필수] 개인정보의 제3자 제공 동의', + required: true, + }, + { id: 'marketing', label: '[선택] 마케팅 정보 수신 동의', required: false }, +]; + +interface ProfileAgreeProps { + agreements: Record; + onAgreementsChange: (agreements: Record) => void; +} + +export function ProfileAgree({ + agreements, + onAgreementsChange, +}: ProfileAgreeProps) { + const allAgreed = agreementItems.every((item) => agreements[item.id]); + + function handleAllAgree() { + const newValue = !allAgreed; + const newAgreements = { + terms: newValue, + privacy: newValue, + thirdParty: newValue, + marketing: newValue, + }; + onAgreementsChange(newAgreements); + } + + function handleIndividualAgree(id: string) { + const newAgreements = { + ...agreements, + [id]: !agreements[id], + }; + onAgreementsChange(newAgreements); + } + + return ( +
+
+ +
+ 전체 동의하기 +
+
+ +
+ {agreementItems.map((item) => ( +
+
+ + + {item.label} + +
+ + + +
+ ))} +
+
+ ); +} diff --git a/src/feature/onboarding/components/ProfileImage.tsx b/src/feature/onboarding/components/ProfileImage.tsx new file mode 100644 index 00000000..d09ac44d --- /dev/null +++ b/src/feature/onboarding/components/ProfileImage.tsx @@ -0,0 +1,141 @@ +'use client'; +import { DrawerClose } from '@/components/ui/drawer'; +const BottomSheetModal = dynamic( + () => import('@/global/components/modal/BottomSheetModal'), + { ssr: false }, +); +import { Pencil } from 'lucide-react'; +import Image from 'next/image'; +import { memo, useState } from 'react'; +import { useGetAllProfiles } from '../hooks/useGetAllProfile'; +import dynamic from 'next/dynamic'; + +interface ProfileImageProps { + selectedImage: string | null; + onImageSelect: (image: string) => void; +} + +function ProfileImage({ selectedImage, onImageSelect }: ProfileImageProps) { + const [shouldFetchProfiles, setShouldFetchProfiles] = useState(false); + + const { data, isLoading, isError } = useGetAllProfiles(shouldFetchProfiles); + + const PROFILE_IMAGES: Record = { + P1: 'https://say-cheese-profile.edge.naverncp.com/profile/sign_up_profile_1.jpg', + }; + + const imageList = + data?.opts?.filter((img) => img.imageCode && img.profileImageUrl) ?? []; + + const getCurrentImageUrl = (): string => { + if (imageList.length > 0 && selectedImage) { + const foundImage = imageList.find( + (img) => img.imageCode === selectedImage, + ); + if (foundImage?.profileImageUrl) { + return foundImage.profileImageUrl; + } + } + + if (selectedImage && PROFILE_IMAGES[selectedImage]) { + return PROFILE_IMAGES[selectedImage]; + } + + return PROFILE_IMAGES['P1']; + }; + + const currentImage = getCurrentImageUrl(); + + return ( +
+
+ + 프로필 이미지 +
+ +
+ + } + showCloseButton={false} + className='h-90 px-5' + showHandle={true} + dismissible={true} + onOpenChange={(isOpen) => { + // 모달이 열리면 API fetch 활성화 (한번만) + if (isOpen && !shouldFetchProfiles) { + setShouldFetchProfiles(true); + } + }} + > + {isLoading ? null : isError ? ( +
+ 이미지 목록을 불러오지 못했습니다. +
+ ) : ( +
+ {imageList.map((img, index) => { + const url = img.profileImageUrl; + const key = img.imageCode || url; + return ( + + + + ); + })} +
+ )} +
+
+ + {/* CSS 애니메이션 정의 */} + +
+ ); +} + +export default memo(ProfileImage); diff --git a/src/feature/onboarding/components/ScreenOnBoarding.tsx b/src/feature/onboarding/components/ScreenOnBoarding.tsx new file mode 100644 index 00000000..56256719 --- /dev/null +++ b/src/feature/onboarding/components/ScreenOnBoarding.tsx @@ -0,0 +1,109 @@ +'use client'; +import { ProfileAgree } from '@/feature/onboarding/components/ProfileAgree'; +import ProfileImage from '@/feature/onboarding/components/ProfileImage'; +import { useOnBoardingMutation } from '@/feature/onboarding/hooks/useOnBoardingMutation'; +import LogoHeader from '@/global/components/header/LogoHeader'; +import LongButton from '@/global/components/LongButton'; +import Toast from '@/global/components/toast/Toast'; +import XInput from '@/global/components/XInput'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { useState } from 'react'; + +export default function ScreenOnBoarding() { + const searchParams = useSearchParams(); + const router = useRouter(); + + const [selectedImage, setSelectedImage] = useState('P1'); + + const nameFromQuery = searchParams.get('name') || ''; + const [nickname, setNickname] = useState( + decodeURIComponent(nameFromQuery), + ); + + const [nicknameError, setNicknameError] = useState(''); + + const handleNicknameChange = (value: string) => { + // 한글(완성형+자음+모음), 영문, 숫자, 공백만 허용하는 정규식 + const validPattern = /^[가-힣ㄱ-ㅎㅏ-ㅣa-zA-Z0-9 ]*$/; + + if (!validPattern.test(value)) { + setNicknameError('10글자 이내의 한글, 영문, 숫자만 쓸 수 있어요'); + } else { + setNicknameError(''); + } + + setNickname(value); + }; + + const [agreements, setAgreements] = useState>({ + terms: false, + privacy: false, + thirdParty: false, + marketing: false, + }); + + const requiredAgreements = ['terms', 'privacy', 'thirdParty']; + const isRequiredAgreed = requiredAgreements.every((key) => agreements[key]); + + const isFormComplete = + selectedImage && + nickname.trim() !== '' && + nicknameError === '' && + isRequiredAgreed; + + const { mutate, status } = useOnBoardingMutation(); + const isLoading = status === 'pending'; + + const handleSubmit = () => { + if (!isFormComplete || isLoading) return; + mutate( + { + name: nickname, + imageCode: selectedImage, + isServiceAgreement: agreements.terms, + isUserInfoAgreement: agreements.privacy, + isMarketingAgreement: agreements.marketing, + isThirdPartyAgreement: agreements.thirdParty, + }, + { + onSuccess: () => { + router.push('/onboarding/complete'); + }, + onError: (error) => { + Toast.alert('가입에 실패했어요. 잠시 후 다시 시도해주세요.'); + console.error('온보딩 실패:', error); + }, + }, + ); + }; + + return ( +
+ + + + + + +
+ ); +} diff --git a/src/feature/onboarding/components/ScreenOnBoardingComplete.tsx b/src/feature/onboarding/components/ScreenOnBoardingComplete.tsx new file mode 100644 index 00000000..ac05e08d --- /dev/null +++ b/src/feature/onboarding/components/ScreenOnBoardingComplete.tsx @@ -0,0 +1,44 @@ +'use client'; +import LongButton from '@/global/components/LongButton'; +import { clearEntryCookie, getEntryCookie } from '@/global/utils/cookies'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; +import { useCallback } from 'react'; + +export default function ScreenOnboardingComplete() { + const router = useRouter(); + + const handleStartClick = useCallback(() => { + const entry = getEntryCookie(); + + if (entry === 'create-album') { + router.push('/create-album'); + } else { + router.push('/main'); + } + + clearEntryCookie(); + }, [router]); + return ( +
+ 환영합니다 + + 치이이즈에 +
+ 오신 걸 환영해요 +
+ +
+ ); +} diff --git a/src/feature/onboarding/components/TermContent.tsx b/src/feature/onboarding/components/TermContent.tsx new file mode 100644 index 00000000..139a4d1a --- /dev/null +++ b/src/feature/onboarding/components/TermContent.tsx @@ -0,0 +1,214 @@ +import { FC } from 'react'; // React 19+에서 타입 추론이 강력해졌지만, FC로 명시해서 타입 안전성 강화 + +const TermsComponent: FC = () => ( +
+
+
+
+ 이용약관 동의 +
+
+ 본 약관은 치이이즈(이하 "회사")가 제공하는 웹 기반 사진 공유 + 서비스(이하 "서비스")의 이용과 관련하여 회사와 이용자 간의 + 권리, 의무 및 책임 사항, 기타 필요한 사항을 규정함을 목적으로 합니다. +
+
+
+
+ 제1조 (서비스의 내용) +
+
+ 이 서비스는 사용자가 QR코드 또는 URL을 통해 웹페이지에 접속하여 사진을 + 업로드하고, 다른 사용자와 함께 공유하며, 7일이 경과한 후 자동으로 + 삭제되는 단기형 공유 앨범을 제공합니다. 본 서비스는 별도의 + 애플리케이션 설치 없이 웹 환경에서 이용할 수 있습니다. +
+
+
+
+ 제2조 (이용자의 의무) +
+
+ 이용자는 서비스를 이용함에 있어 다음 각 호의 행위를 하여서는 아니 + 됩니다. +

+   ·  타인의 개인정보를 무단으로 수집하거나 + 부정하게 사용하는 행위 +

+

+   ·  불법적이거나 사회질서에 반하는 내용의 사진 + 또는 글을 게시하는 행위 +

+

+   ·  회사 및 제3자의 저작권, 초상권 등 타인의 + 권리를 침해하는 행위 +

+

+   ·  본 서비스를 영리 목적 또는 부정한 목적으로 + 이용하는 행위 +

+
+ 이용자는 본 서비스의 취지에 맞게 타인에게 불쾌감을 주거나 불법적인 + 콘텐츠를 업로드하지 않아야 하며, 위반 시 이용이 제한될 수 있습니다. +
+
+
+
+ 제3조 (서비스의 변경 및 중단) +
+
+ 회사는 서비스의 개선을 위하여 제공 중인 서비스의 전부 또는 일부를 + 변경하거나 중단할 수 있습니다. +
+ 회사는 서비스 중단으로 발생하는 데이터 손실에 대하여 별도의 보상을 + 하지 않으며, 이용자는 7일의 유효기간 내에 필요한 데이터를 직접 + 저장해야 합니다. +
+
+
+
+ 제4조 (면책) +
+
+ 회사는 천재지변, 시스템 장애, 통신 두절 등 불가항력적인 사유로 인한 + 서비스 중단 및 데이터 손실에 대하여 책임을 지지 않습니다. 이용자가 + 업로드한 모든 콘텐츠의 저작권 및 책임은 전적으로 해당 이용자에게 + 있으며, 회사는 그 내용에 대해 일체의 책임을 지지 않습니다. +
+
+
+
+); + +const PrivacyComponent: FC = () => ( +
+
+
+
+ 개인정보 수집 및 이용 동의 +
+
+ 회사는 서비스 제공을 위하여 다음과 같은 개인정보를 수집하고 + 이용합니다. +

① 수집 항목

+

+   ·  필수항목: 닉네임, 로그인 계정(카카오 등 + 간편 로그인 정보), +
+      업로드된 사진 및 메타데이터, 접속 + 로그, 쿠키 등 +

+

+   ·  선택항목: 이메일 주소(이벤트 및 알림 + 수신용), 프로필 이미지 +

+
+

② 수집 및 이용 목적

+

1. 서비스 이용자 식별 및 참여 관리

+

2. 사진 업로드, 다운로드 등 주요 기능 제공

+

3. 불법·비정상 이용행위 방지 및 보안 관리

+

4. 서비스 개선, 이용 통계 및 분석

+
+

③ 보유 및 이용 기간

+

+   ·  업로드된 사진 및 관련 데이터는 +
+      앨범 생성 시점으로부터 7일간 보관 후 + 자동 삭제됩니다. +

+

+   ·  로그인 계정 정보 등 식별정보는
+      회원 탈퇴 시 또는 이용 목적이 달성된 + 후 즉시 파기됩니다. +

+
④ 이용자는 개인정보의 열람, 정정, 삭제를 요청할 수 있으며, + 회사는 관련 법령에 따라 이에 성실히 응합니다. +
+
+
+
+); + +const ThirdPartyComponent: FC = () => ( +
+
+
+
+ 개인정보의 제3자 제공 동의 +
+
+ 회사는 서비스의 안정적 운영과 기능 제공을 위하여, 이용자의 개인정보를 + 아래와 같이 제3자에게 제공합니다. 회사는 정보통신망 이용촉진 및 + 정보보호 등에 관한 법률 및 개인정보보호법 등 관련 법령을 준수하며, + 이용자의 개인정보를 목적 외로 제공하지 않습니다. +
+
+ 회사는 이용자가 업로드한 사진과 관련 데이터를 안전하게 저장하고 + 관리하기 위하여, 클라우드 서비스 제공업체인 네이버클라우드플랫폼(Naver + Cloud Platform)에 일부 정보를 제공합니다. 네이버클라우드플랫폼은 서버 + 저장, 백업, 데이터 관리 등의 목적으로만 해당 정보를 이용하며, 회사의 + 지침에 따라 엄격하게 관리합니다. 제공되는 항목은 업로드된 사진, 앨범 + 식별 코드, 업로드 시각 등 서비스 운영에 필요한 최소한의 데이터이며, + 해당 정보는 앨범 생성 시점으로부터 7일간 보관된 후 자동으로 + 삭제됩니다. +
+
+ 또한, 간편 로그인 기능 제공을 위해 카카오(주)에 로그인 계정 + 정보(이메일, 닉네임 등)가 제공됩니다. 제공된 정보는 간편 로그인 및 + 사용자 인증을 위한 용도로만 이용되며, 이용자가 탈퇴하거나 계정 연동을 + 해제할 경우 즉시 삭제됩니다. +
+
+ 서비스 품질 개선과 이용 통계 분석을 위해 Google Analytics에 접속 로그 + 및 이용 패턴 등 익명화된 정보가 제공될 수 있습니다. 이러한 정보는 + 개인을 식별할 수 없는 형태로 수집되며, 통계적 분석 목적으로만 사용된 + 뒤 익명화 후 6개월간 보관됩니다. +
+
+ 회사는 위의 목적 외에는 이용자의 개인정보를 제3자에게 제공하지 않으며, + 새로운 제휴 또는 제공이 필요한 경우 이용자에게 사전 고지 후 별도의 + 동의를 받습니다. +
+
+
+
+); + +const MarketingComponent: FC = () => ( +
+
+
+
+ 마케팅 정보 수신 동의 +
+
+ 회사는 이벤트, 신규 서비스, 프로모션 등 유용한 정보를 이메일, + 문자메시지, 카카오 알림톡 등의 방법으로 발송할 수 있습니다. +
이용자는 마케팅 정보 수신에 동의하지 않아도 기본 서비스 이용에 + 제한이 없습니다. +
마케팅 정보의 수신 동의는 언제든 설정 메뉴 또는 수신 거부 + 링크를 통해 철회할 수 있습니다. +
+
+
+
+); + +export const TermContent = { + terms: { + title: '이용약관 동의', + content: TermsComponent, + }, + privacy: { + title: '개인정보 수집 및 이용 동의', + content: PrivacyComponent, + }, + thirdParty: { + title: '개인정보의 제3자 제공 동의', + content: ThirdPartyComponent, + }, + marketing: { + title: '마케팅 정보 수신 동의', + content: MarketingComponent, + }, +}; diff --git a/src/feature/onboarding/hooks/useGetAllProfile.ts b/src/feature/onboarding/hooks/useGetAllProfile.ts new file mode 100644 index 00000000..ff857470 --- /dev/null +++ b/src/feature/onboarding/hooks/useGetAllProfile.ts @@ -0,0 +1,18 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery } from '@tanstack/react-query'; +const getAllProfile = async () => { + const data = await api.get({ + path: EP.user.userProfileImages(), + }); + return data.result; +}; + +export function useGetAllProfiles(enabled: boolean = true) { + return useQuery({ + queryKey: [EP.user.userProfileImages()], + queryFn: getAllProfile, + staleTime: 36000, + enabled, + }); +} diff --git a/src/feature/onboarding/hooks/useOnBoardingMutation.test.tsx b/src/feature/onboarding/hooks/useOnBoardingMutation.test.tsx new file mode 100644 index 00000000..8ce1a8a3 --- /dev/null +++ b/src/feature/onboarding/hooks/useOnBoardingMutation.test.tsx @@ -0,0 +1,67 @@ +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; +import { beforeEach, describe, expect, it, vi } from 'vitest'; +import { useOnBoardingMutation } from './useOnBoardingMutation'; + +// Mock api +vi.mock('@/global/utils/api', () => ({ + api: { + post: vi.fn(), + }, +})); + +// Mock useMutation +vi.mock('@tanstack/react-query', () => ({ + useMutation: vi.fn(), +})); + +describe('useOnBoardingMutation', () => { + beforeEach(() => { + vi.clearAllMocks(); + }); + + it('should call useMutation with correct mutationFn', () => { + useOnBoardingMutation(); + + expect(useMutation).toHaveBeenCalledWith( + expect.objectContaining({ + mutationFn: expect.any(Function), + }), + ); + }); + + it('should call api.post when mutationFn is executed', async () => { + // Capture the mutationFn passed to useMutation + // eslint-disable-next-line @typescript-eslint/no-explicit-any + let capturedMutationFn: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (useMutation as any).mockImplementation(({ mutationFn }: any) => { + capturedMutationFn = mutationFn; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + return {} as any; + }); + + useOnBoardingMutation(); + + const mockPayload = { + name: 'Test User', + imageCode: 'img-123', + isServiceAgreement: true, + isUserInfoAgreement: true, + isMarketingAgreement: false, + isThirdPartyAgreement: true, + }; + + const mockResponse = { result: { success: true } }; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + (api.post as any).mockResolvedValue(mockResponse); + + // Execute the captured mutationFn + await capturedMutationFn(mockPayload); + + expect(api.post).toHaveBeenCalledWith({ + path: expect.stringContaining('/user/onboarding'), + body: mockPayload, + }); + }); +}); diff --git a/src/feature/onboarding/hooks/useOnBoardingMutation.ts b/src/feature/onboarding/hooks/useOnBoardingMutation.ts new file mode 100644 index 00000000..14af9ae9 --- /dev/null +++ b/src/feature/onboarding/hooks/useOnBoardingMutation.ts @@ -0,0 +1,26 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +type OnboardingPayload = { + name: string; + imageCode: string; + isServiceAgreement: boolean; + isUserInfoAgreement: boolean; + isMarketingAgreement: boolean; + isThirdPartyAgreement: boolean; +}; + +async function postUserOnboarding(payload: OnboardingPayload) { + const res = await api.post({ + path: EP.user.userOnboarding(), + body: payload, + }); + return res.result; +} + +export function useOnBoardingMutation() { + return useMutation({ + mutationFn: (payload: OnboardingPayload) => postUserOnboarding(payload), + }); +} diff --git a/src/feature/photo-detail/components/FooterPhotoDetail.tsx b/src/feature/photo-detail/components/FooterPhotoDetail.tsx new file mode 100644 index 00000000..b838e6ba --- /dev/null +++ b/src/feature/photo-detail/components/FooterPhotoDetail.tsx @@ -0,0 +1,170 @@ +'use client'; +import { EP } from '@/global/api/ep'; +import BottomSheetModal from '@/global/components/modal/BottomSheetModal'; +import Toast from '@/global/components/toast/Toast'; +import { downloadFile } from '@/global/utils/downloadFile'; +import { getDeviceType } from '@/global/utils/getDeviceType'; +import { shareImage } from '@/global/utils/image/shareImage'; +import { useQueryClient } from '@tanstack/react-query'; +import { Download, Heart, Info } from 'lucide-react'; +import { useState } from 'react'; +import { usePhotoDownloadMutation } from '../hooks/usePhotoDownloadMutation'; +import { usePhotoLikedMutation } from '../hooks/usePhotoLikedMutation'; +import { usePhotoUnlikedMutation } from '../hooks/usePhotoUnlikedMutation'; +import { updateCacheAlbumPhotosLike } from '../modules/updateCacheAlbumPhotosLike'; +import ListPhotoLikers from './ListPhotoLikers'; +import SectionPhotoData from './SectionPhotoData'; + +interface FooterPhotoDetailProps { + albumId: string; + photoId: number; + isLiked: boolean; + likeCnt: number; + isRecentlyDownloaded: boolean; + imageUrl: string | undefined; +} + +export default function FooterPhotoDetail({ + albumId, + photoId, + isLiked, + likeCnt, + isRecentlyDownloaded, + imageUrl, +}: FooterPhotoDetailProps) { + const queryClient = useQueryClient(); + const [isDownloading, setIsDownloading] = useState(false); + const [isPhotoInfoOpen, setIsPhotoInfoOpen] = useState(false); + const { mutateAsync: mutateAsyncLike, isPending: isLiking } = + usePhotoLikedMutation(); + const { mutateAsync: mutateAsyncUnlike, isPending: isUnliking } = + usePhotoUnlikedMutation(); + const { mutateAsync: mutateAsyncDownload } = usePhotoDownloadMutation(); + + const handleDeepToggle = async (): Promise => { + try { + if (isLiked) { + if (!isUnliking) await mutateAsyncUnlike(photoId); + } else { + if (!isLiking) await mutateAsyncLike(photoId); + } + + updateCacheAlbumPhotosLike({ + albumId, + isCurrentlyLiked: isLiked, + photoId, + queryClient, + }); + queryClient.invalidateQueries({ + queryKey: [EP.album.albumPhotosLikers(albumId, photoId)], + }); + queryClient.invalidateQueries({ + queryKey: [EP.album.likedPhotos(albumId)], + }); + } catch (e) { + console.error(e); + Toast.alert(`좋에요에 실패하였습니다.`); + } + }; + + const handleDownload = async (): Promise => { + if (!imageUrl) return; + if (isRecentlyDownloaded) { + Toast.alert(`금방 다운받은 사진이에요.\n1시간 뒤에 다시 시도하세요.`); + return; + } + if (isDownloading) return; + + try { + setIsDownloading(true); + const deviceType = getDeviceType(); + const fileName = `IMG_${photoId}`; + + if (deviceType === 'ios') { + shareImage({ + imageUrls: imageUrl, + imageTitle: fileName, + onSuccess: () => { + mutateAsyncDownload({ albumId, photoIds: [photoId] }); + }, + onError: () => { + downloadFile(imageUrl, fileName); + }, + }); + } else { + await downloadFile(imageUrl, fileName); + mutateAsyncDownload({ albumId, photoIds: [photoId] }); + } + } catch (e) { + console.log(e); + Toast.alert('사진을 준비하는 중 오류가 발생했습니다.'); + } finally { + setIsDownloading(false); + } + }; + + return ( +
+ + + + } + > + setIsPhotoInfoOpen(false)} + /> + + + + +
+ + + + + {likeCnt} + + + } + > + + +
+
+ ); +} diff --git a/src/feature/photo-detail/components/HeaderPhotoDetail.tsx b/src/feature/photo-detail/components/HeaderPhotoDetail.tsx new file mode 100644 index 00000000..99a0639b --- /dev/null +++ b/src/feature/photo-detail/components/HeaderPhotoDetail.tsx @@ -0,0 +1,40 @@ +'use client'; +import { DEFAULT_PROFILE_IMAGE } from '@/global/constants/images'; +import { X } from 'lucide-react'; +import { useRouter } from 'next/navigation'; + +interface HeaderPhotoDetailProps { + profileImageUrl?: string; + name?: string; +} + +export default function HeaderPhotoDetail({ + profileImageUrl, + name, +}: HeaderPhotoDetailProps) { + const router = useRouter(); + + const handleClose = (): void => { + router.back(); + }; + + return ( +
+
+ 프로필 사진 +
+ + {name} + + +
+ ); +} diff --git a/src/feature/photo-detail/components/ItemMemberData.tsx b/src/feature/photo-detail/components/ItemMemberData.tsx new file mode 100644 index 00000000..291dd52c --- /dev/null +++ b/src/feature/photo-detail/components/ItemMemberData.tsx @@ -0,0 +1,49 @@ +import { DEFAULT_PROFILE_IMAGE } from '@/global/constants/images'; + +interface ItemMemberDataProps { + profileImageUrl: string | undefined; + nickname: string; + isMe: boolean; + isMaker: boolean; +} + +export default function ItemMemberData({ + profileImageUrl, + nickname, + isMe, + isMaker, +}: ItemMemberDataProps) { + return ( +
+
+ {`${nickname}의 +
+ +
+ + {nickname} + + +
+ {isMe && ( + + 나 + + )} + + {isMaker && ( + + 메이커 + + )} +
+
+
+ ); +} diff --git a/src/feature/photo-detail/components/ListPhotoLikers.tsx b/src/feature/photo-detail/components/ListPhotoLikers.tsx new file mode 100644 index 00000000..9d062ae2 --- /dev/null +++ b/src/feature/photo-detail/components/ListPhotoLikers.tsx @@ -0,0 +1,35 @@ +import { usePhotoLikersQuery } from '../hooks/usePhotoLikersQuery'; +import ItemMemberData from './ItemMemberData'; + +interface ListPhotoLikersProps { + albumId: string; + photoId: number; +} + +export default function ListPhotoLikers({ + albumId, + photoId, +}: ListPhotoLikersProps) { + const { data, isPending, isError } = usePhotoLikersQuery({ + albumId, + photoId, + }); + + if (isPending) return null; + if (isError) return null; + if (!data || !data.photoLikers) return null; + + return ( +
+ {data.photoLikers.map(({ name, profileImageUrl, role, isMe }, index) => ( + + ))} +
+ ); +} diff --git a/src/feature/photo-detail/components/MainPhotoDetail.tsx b/src/feature/photo-detail/components/MainPhotoDetail.tsx new file mode 100644 index 00000000..fe2dc209 --- /dev/null +++ b/src/feature/photo-detail/components/MainPhotoDetail.tsx @@ -0,0 +1,68 @@ +import { PhotoListResponseSchema } from '@/global/api/ep'; +import Spinner from '@/global/components/Spinner'; +import dynamic from 'next/dynamic'; +import { useState } from 'react'; +import FooterPhotoDetail from './FooterPhotoDetail'; +import HeaderPhotoDetail from './HeaderPhotoDetail'; +const SwiperPhotoList = dynamic(() => import('./SwiperPhotoList'), { + ssr: false, + loading: () => ( +
+ +
+ ), +}); + +interface MainPhotoDetailProps { + images: PhotoListResponseSchema[]; + albumId: string; + photoId: number | null; +} + +export default function MainPhotoDetail({ + albumId, + images, + photoId, +}: MainPhotoDetailProps) { + const [activeIndex, setActiveIndex] = useState( + findImageIndexByPhotoId(images, photoId), + ); + + const changeActiveIndex = (newIndex: number): void => { + setActiveIndex(newIndex); + }; + + const activeImage = images[activeIndex]; + if (!activeImage) return null; + + return ( + <> + + + + + ); +} + +function findImageIndexByPhotoId( + images: PhotoListResponseSchema[], + targetPhotoId: number | null, +): number { + if (!targetPhotoId) return -1; + + return images.findIndex((image) => image.photoId === targetPhotoId); +} diff --git a/src/feature/photo-detail/components/ScreenPhotoDetail.tsx b/src/feature/photo-detail/components/ScreenPhotoDetail.tsx new file mode 100644 index 00000000..42392372 --- /dev/null +++ b/src/feature/photo-detail/components/ScreenPhotoDetail.tsx @@ -0,0 +1,35 @@ +'use client'; + +import { PhotoSorting } from '@/global/api/ep'; +import { useSearchParams } from 'next/navigation'; +import { useAlbumPhotosInfiniteQuery } from '../hooks/useAlbumPhotosInfiniteQuery'; +import MainPhotoDetail from './MainPhotoDetail'; + +interface ScreenPhotoDetailProps { + albumId: string; +} + +export default function ScreenPhotoDetail({ albumId }: ScreenPhotoDetailProps) { + const searchParams = useSearchParams(); + + const sort: PhotoSorting = + (searchParams.get('sort') as PhotoSorting) || 'CREATED_AT'; + const photoIdParam = searchParams.get('photoId'); + + const { items: images } = useAlbumPhotosInfiniteQuery({ + code: albumId, + size: 2000, + sorting: sort, + }); + + if (images.length === 0) return null; + + const photoId = + photoIdParam === null ? images[0].photoId : Number(photoIdParam); + + return ( +
+ +
+ ); +} diff --git a/src/feature/photo-detail/components/SectionPhotoData.tsx b/src/feature/photo-detail/components/SectionPhotoData.tsx new file mode 100644 index 00000000..c3b598ad --- /dev/null +++ b/src/feature/photo-detail/components/SectionPhotoData.tsx @@ -0,0 +1,115 @@ +import { EP } from '@/global/api/ep'; +import ConfirmModal from '@/global/components/modal/ConfirmModal'; +import { useQueryClient } from '@tanstack/react-query'; +import { useDeleteAlbumPhotoMutation } from '../hooks/useDeleteAlbumPhotoMutation'; +import { usePhotoDetailQuery } from '../hooks/usePhotoDetailQuery'; + +interface SectionPhotoDataProps { + albumId: string; + photoId: number; + onAfterDelete?: () => void; +} + +// 촬영 시각: 사진 EXIF 시간 그대로 표시 (타임존 변환 안 함) +const formatCaptureTime = (isoString?: string): string => { + if (!isoString) return ''; + + // ISO 문자열에서 직접 파싱 (타임존 변환 없이) + const match = isoString.match( + /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})/, + ); + if (!match) return '정보 없음'; + + const [, year, month, day, hour, minute] = match; + + return `${year}년 ${month}월 ${day}일 ${hour}시 ${minute}분`; +}; + +// 업로드 시각: UTC를 로컬 시간(KST)으로 변환 +const formatKoreanDateTime = (isoString?: string): string => { + if (!isoString) return ''; + + // If the string doesn't end with Z and doesn't have a timezone offset, assume it's UTC + const targetDate = + !isoString.endsWith('Z') && !/[+-]\d{2}:\d{2}$/.test(isoString) + ? `${isoString}Z` + : isoString; + + const date = new Date(targetDate); + if (Number.isNaN(date.getTime())) return '정보 없음'; + + const year = date.getFullYear(); + const month = date.getMonth() + 1; + const day = date.getDate(); + const hour = date.getHours(); + const minute = date.getMinutes().toString().padStart(2, '0'); + + return `${year}년 ${month}월 ${day}일 ${hour}시 ${minute}분`; +}; + +export default function SectionPhotoData({ + albumId, + photoId, + onAfterDelete, +}: SectionPhotoDataProps) { + const queryClient = useQueryClient(); + const { data, isPending, isError } = usePhotoDetailQuery({ + albumId, + photoId, + }); + const { mutateAsync } = useDeleteAlbumPhotoMutation(); + + if (isPending) return null; + if (isError) return null; + + const handleDeleteClick = async () => { + try { + await mutateAsync({ albumId, photoId }); + queryClient.invalidateQueries({ queryKey: [EP.album.photos(albumId)] }); + } finally { + onAfterDelete?.(); + } + }; + + return ( +
+
+
+
업로드한 사람
+
{data?.name}
+
+
+
촬영 시각
+
+ {formatCaptureTime(data?.captureTime)} +
+
+
+
업로드 시각
+
+ {formatKoreanDateTime(data?.createdAt)} +
+
+
+ + {data?.canDelete && ( + + 사진 삭제하기 + + } + /> + )} +
+ ); +} diff --git a/src/feature/photo-detail/components/SwiperPhotoList.tsx b/src/feature/photo-detail/components/SwiperPhotoList.tsx new file mode 100644 index 00000000..6789c7e3 --- /dev/null +++ b/src/feature/photo-detail/components/SwiperPhotoList.tsx @@ -0,0 +1,166 @@ +'use client'; + +import { PhotoListResponseSchema } from '@/global/api/ep'; +import { useCallback, useEffect, useState } from 'react'; +import type { Swiper as SwiperType } from 'swiper'; +import 'swiper/css'; +import { Swiper, SwiperSlide } from 'swiper/react'; +import { calcThumbSwiperCenterOffset } from '../util/calcThumbSwiperCenterOffset'; + +interface SwiperPhotoListProps { + images?: PhotoListResponseSchema[]; + activeIndex: number; + changeActiveIndex: (newIndex: number) => void; +} + +export default function SwiperPhotoList({ + activeIndex, + changeActiveIndex, + images = [], +}: SwiperPhotoListProps) { + const [mainSwiper, setMainSwiper] = useState(null); + const [thumbSwiper, setThumbSwiper] = useState(null); + const [thumbOffset, setThumbOffset] = useState(0); + + const updateThumbOffset = useCallback(() => { + const vw = window.innerWidth; + setThumbOffset( + calcThumbSwiperCenterOffset({ + viewportWidth: vw, + activeMargin: 12, + activeWidth: 30, + inactiveWidth: 15, + inactiveMargin: 2, + index: activeIndex, + }), + ); + }, [activeIndex]); + + useEffect(() => { + updateThumbOffset(); + window.addEventListener('resize', updateThumbOffset); + return () => window.removeEventListener('resize', updateThumbOffset); + }, [updateThumbOffset]); + + useEffect(() => { + if (!thumbSwiper || thumbSwiper.destroyed) return; + + thumbSwiper.slideTo(activeIndex); + requestAnimationFrame(() => { + thumbSwiper.setTranslate(thumbOffset); + }); + }, [activeIndex, thumbOffset, thumbSwiper]); + + return ( + <> +
+ {/* 위: 메인 이미지 Swiper */} +
+ { + const idx = sw.activeIndex; + changeActiveIndex(idx); + if (thumbSwiper && !thumbSwiper.destroyed) { + thumbSwiper.slideTo(sw.activeIndex); + } + }} + onTap={(swiper, event) => { + if (!swiper || swiper.destroyed) return; + // 마우스/터치 겸용으로 clientX 추출 + const e = event as MouseEvent | TouchEvent; + let clientX: number | null = null; + + if ('clientX' in e) { + clientX = e.clientX; + } else if ('changedTouches' in e && e.changedTouches.length > 0) { + clientX = e.changedTouches[0].clientX; + } + + if (clientX == null) return; + + const { left, width } = swiper.el.getBoundingClientRect(); + const clickPosition = clientX - left; + + if (clickPosition < width / 2) { + swiper.slidePrev(); + } else { + swiper.slideNext(); + } + }} + > + {images.map(({ thumbnailUrl, photoId }, i) => { + const isActive = activeIndex === i; + return ( + +
+ {`photo-${i}`} +
+
+ ); + })} +
+
+ + {/* 아래: 썸네일 컨트롤러 */} +
+ + {images.map(({ thumbnailUrl, photoId }, i) => { + const isActive = activeIndex === i; + return ( + { + changeActiveIndex(i); + mainSwiper?.slideTo(i); + thumbSwiper?.slideTo(i); + }} + > + {/* TODO : 이미지 아직 불러오는 중일때 스켈레톤 띄우기 */} + {`thumb-${i}`} + + ); + })} + +
+
+ + ); +} diff --git a/src/feature/photo-detail/hooks/useAlbumPhotosInfiniteQuery.ts b/src/feature/photo-detail/hooks/useAlbumPhotosInfiniteQuery.ts new file mode 100644 index 00000000..1138fe6c --- /dev/null +++ b/src/feature/photo-detail/hooks/useAlbumPhotosInfiniteQuery.ts @@ -0,0 +1,62 @@ +import { ApiReturns, EP, PhotoSorting } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useInfiniteQuery } from '@tanstack/react-query'; + +interface FetchPageParams { + code: string; + pageParam: number; + /** 페이지 사이즈 (기본 20) */ + size: number; + /** API가 받는 정렬 형식: 예) 'CREATED_AT' */ + sorting: PhotoSorting; +} + +const fetchAlbumPhotosPage = async ({ + code, + pageParam, + size, + sorting, +}: FetchPageParams) => { + const res = await api.get({ + path: EP.album.photos(code), + params: { page: pageParam, size, sorting }, + }); + + if (!res.result) throw Error('result가 없습니다.'); + + return { ...res.result, page: pageParam }; +}; + +interface UseAlbumPhotosInfiniteQueryProps { + code: string; + size?: number; + sorting?: PhotoSorting; + enabled?: boolean; + refetchOnMount?: boolean | 'always'; +} + +export function useAlbumPhotosInfiniteQuery({ + code, + size = 20, + sorting = 'CREATED_AT', + enabled = true, + refetchOnMount = true, +}: UseAlbumPhotosInfiniteQueryProps) { + const query = useInfiniteQuery({ + queryKey: [EP.album.photos(code), size, sorting], + initialPageParam: 0, + enabled: enabled && !!code, + queryFn: ({ pageParam }) => + fetchAlbumPhotosPage({ code, pageParam, size, sorting }), + getNextPageParam: (lastPage) => + lastPage.hasNext ? lastPage.page + 1 : undefined, + refetchOnMount, + }); + + const items = query.data?.pages.flatMap((p) => p.responses ?? []) ?? []; + + return { + ...query, + items, + }; +} diff --git a/src/feature/photo-detail/hooks/useAlbumPhotosLikedInfiniteQuery.ts b/src/feature/photo-detail/hooks/useAlbumPhotosLikedInfiniteQuery.ts new file mode 100644 index 00000000..b1654f1e --- /dev/null +++ b/src/feature/photo-detail/hooks/useAlbumPhotosLikedInfiniteQuery.ts @@ -0,0 +1,75 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useInfiniteQuery } from '@tanstack/react-query'; + +type AlbumPhotosLikedPage = NonNullable; +type AlbumPhotosLikedItem = AlbumPhotosLikedPage['responses'][number]; + +interface FetchPageParams { + code: string; + pageParam: number; + /** 페이지 사이즈 (기본 20) */ + size: number; +} + +const fetchAlbumPhotosPage = async ({ + code, + pageParam, + size, +}: FetchPageParams): Promise => { + const res = await api.get({ + path: EP.album.likedPhotos(code), + params: { page: pageParam, size }, + }); + + const result: AlbumPhotosLikedPage = + res.result ?? createEmptyPage({ pageParam }); + + return { ...result, page: pageParam }; +}; + +const createEmptyPage = ({ + pageParam, +}: { + pageParam: number; +}): AlbumPhotosLikedPage => ({ + responses: [], + listSize: 0, + isFirst: pageParam === 0, + isLast: true, + hasNext: false, +}); + +interface UseAlbumPhotosLikedInfiniteQueryProps { + code: string; + size?: number; + enabled?: boolean; + refetchOnMount?: boolean | 'always'; +} + +export function useAlbumPhotosLikedInfiniteQuery({ + code, + size = 20, + enabled = true, + refetchOnMount, +}: UseAlbumPhotosLikedInfiniteQueryProps) { + const query = useInfiniteQuery({ + queryKey: [EP.album.likedPhotos(code), size], + initialPageParam: 0, + enabled: enabled && !!code, + queryFn: ({ pageParam }) => fetchAlbumPhotosPage({ code, pageParam, size }), + getNextPageParam: (lastPage) => + lastPage.hasNext ? lastPage.page + 1 : undefined, + refetchOnMount, + }); + + const items: AlbumPhotosLikedItem[] = + query.data?.pages.flatMap((p) => p.responses ?? []) ?? []; + + return { + ...query, + items, + }; +} + +export type { AlbumPhotosLikedItem }; diff --git a/src/feature/photo-detail/hooks/useDeleteAlbumPhotoMutation.ts b/src/feature/photo-detail/hooks/useDeleteAlbumPhotoMutation.ts new file mode 100644 index 00000000..d8db6977 --- /dev/null +++ b/src/feature/photo-detail/hooks/useDeleteAlbumPhotoMutation.ts @@ -0,0 +1,24 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +const fetchData = async (albumId: string, photoId: number) => { + const res = await api.delete({ + path: EP.album.albumPhoto(albumId, photoId), + }); + return res.result; +}; + +interface DeleteAlbumPhotoMutateProps { + albumId: string; + photoId: number; +} + +export function useDeleteAlbumPhotoMutation() { + const mutation = useMutation({ + mutationFn: ({ albumId, photoId }: DeleteAlbumPhotoMutateProps) => + fetchData(albumId, photoId), + }); + + return mutation; +} diff --git a/src/feature/photo-detail/hooks/usePhotoDetailQuery.ts b/src/feature/photo-detail/hooks/usePhotoDetailQuery.ts new file mode 100644 index 00000000..bfb9b822 --- /dev/null +++ b/src/feature/photo-detail/hooks/usePhotoDetailQuery.ts @@ -0,0 +1,44 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { + keepPreviousData, + useQuery, + UseQueryOptions, +} from '@tanstack/react-query'; + +interface FetchDataProps { + albumId: string; + photoId: number; +} + +const fetchData = async ({ albumId, photoId }: FetchDataProps) => { + const response = await api.get({ + path: EP.album.photoDetail(albumId, photoId), + }); + + return response.result; +}; + +interface UsePhotoDetailQueryProps { + albumId: string; + photoId: number; + options?: Omit< + UseQueryOptions, + 'queryKey' | 'queryFn' + >; +} + +export function usePhotoDetailQuery({ + albumId, + photoId, + options, +}: UsePhotoDetailQueryProps) { + const query = useQuery({ + queryKey: [EP.album.photoDetail(albumId, photoId)], + queryFn: () => fetchData({ albumId, photoId }), + placeholderData: keepPreviousData, + ...options, + }); + + return query; +} diff --git a/src/feature/photo-detail/hooks/usePhotoDownloadMutation.ts b/src/feature/photo-detail/hooks/usePhotoDownloadMutation.ts new file mode 100644 index 00000000..0ee17296 --- /dev/null +++ b/src/feature/photo-detail/hooks/usePhotoDownloadMutation.ts @@ -0,0 +1,37 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { updateCacheAlbumPhotosDownload } from '../modules/updateCacheAlbumPhotosDownload'; + +const fetchData = async ({ + albumId, + photoIds, +}: UsePhotoDownloadMutationProps) => { + const response = await api.post({ + path: EP.photo.presignedDownload(), + body: { code: albumId, photoIds: photoIds }, + }); + + return response.result; +}; + +interface UsePhotoDownloadMutationProps { + albumId: string; + photoIds: number[]; +} + +export function usePhotoDownloadMutation() { + const queryClient = useQueryClient(); + const mutation = useMutation({ + mutationFn: ({ albumId, photoIds }: UsePhotoDownloadMutationProps) => + fetchData({ albumId, photoIds }), + onSuccess: (_data, variables) => { + const { albumId, photoIds } = variables; + photoIds.forEach((photoId) => + updateCacheAlbumPhotosDownload({ albumId, photoId, queryClient }), + ); + }, + }); + + return mutation; +} diff --git a/src/feature/photo-detail/hooks/usePhotoExifQuery.ts b/src/feature/photo-detail/hooks/usePhotoExifQuery.ts new file mode 100644 index 00000000..e02552bf --- /dev/null +++ b/src/feature/photo-detail/hooks/usePhotoExifQuery.ts @@ -0,0 +1,14 @@ +import { getExifFromUrl, PhotoExifInfo } from '@/global/utils/getExifFromUrl'; +import { useQuery } from '@tanstack/react-query'; + +export function usePhotoExifQuery(imageUrl?: string) { + return useQuery({ + queryKey: ['photoExif', imageUrl], + queryFn: () => { + if (!imageUrl) throw new Error('imageUrl이 없습니다.'); + // TODO : exif 가져오는 로직 이슈 있음. 사진 fetch 시 CORS발생 + return getExifFromUrl(imageUrl); + }, + enabled: !!imageUrl, + }); +} diff --git a/src/feature/photo-detail/hooks/usePhotoLikedMutation.ts b/src/feature/photo-detail/hooks/usePhotoLikedMutation.ts new file mode 100644 index 00000000..b5f4dc32 --- /dev/null +++ b/src/feature/photo-detail/hooks/usePhotoLikedMutation.ts @@ -0,0 +1,18 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +async function fetchData(photoId: number) { + const res = await api.post({ + path: EP.photo.like(photoId), + }); + return res.result; +} + +export function usePhotoLikedMutation() { + const mutation = useMutation({ + mutationFn: (photoId: number) => fetchData(photoId), + }); + + return mutation; +} diff --git a/src/feature/photo-detail/hooks/usePhotoLikersQuery.ts b/src/feature/photo-detail/hooks/usePhotoLikersQuery.ts new file mode 100644 index 00000000..e54986f5 --- /dev/null +++ b/src/feature/photo-detail/hooks/usePhotoLikersQuery.ts @@ -0,0 +1,33 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery } from '@tanstack/react-query'; + +interface FetchDataProps { + albumId: string; + photoId: number; +} + +const fetchData = async ({ albumId, photoId }: FetchDataProps) => { + const response = await api.get({ + path: EP.album.albumPhotosLikers(albumId, photoId), + }); + + return response.result; +}; + +interface UsePhotoLikersQueryProps { + albumId: string; + photoId: number; +} + +export function usePhotoLikersQuery({ + albumId, + photoId, +}: UsePhotoLikersQueryProps) { + const query = useQuery({ + queryKey: [EP.album.albumPhotosLikers(albumId, photoId)], + queryFn: () => fetchData({ albumId, photoId }), + }); + + return query; +} diff --git a/src/feature/photo-detail/hooks/usePhotoUnlikedMutation.ts b/src/feature/photo-detail/hooks/usePhotoUnlikedMutation.ts new file mode 100644 index 00000000..c15365c7 --- /dev/null +++ b/src/feature/photo-detail/hooks/usePhotoUnlikedMutation.ts @@ -0,0 +1,18 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useMutation } from '@tanstack/react-query'; + +async function fetchData(photoId: number) { + const res = await api.delete({ + path: EP.photo.unlike(photoId), + }); + return res.result; +} + +export function usePhotoUnlikedMutation() { + const mutation = useMutation({ + mutationFn: (photoId: number) => fetchData(photoId), + }); + + return mutation; +} diff --git a/src/feature/photo-detail/modules/updateCacheAlbumPhotosDownload.ts b/src/feature/photo-detail/modules/updateCacheAlbumPhotosDownload.ts new file mode 100644 index 00000000..930c0dce --- /dev/null +++ b/src/feature/photo-detail/modules/updateCacheAlbumPhotosDownload.ts @@ -0,0 +1,41 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { InfiniteData, QueryClient } from '@tanstack/react-query'; + +interface UpdateCacheAlbumPhotosDownloadParams { + queryClient: QueryClient; + albumId: string; + photoId: number; +} + +export function updateCacheAlbumPhotosDownload({ + queryClient, + albumId, + photoId, +}: UpdateCacheAlbumPhotosDownloadParams) { + queryClient.setQueriesData>( + { queryKey: [EP.album.photos(albumId)] }, + (old) => { + if (!old) return old; + + return { + ...old, + pages: old.pages.map((page) => { + if (!page || !page.responses) return page; + + return { + ...page, + responses: page.responses.map((res) => + res.photoId === photoId + ? { + ...res, + isRecentlyDownloaded: true, + isDownloaded: true, + } + : res, + ), + }; + }), + }; + }, + ); +} diff --git a/src/feature/photo-detail/modules/updateCacheAlbumPhotosLike.ts b/src/feature/photo-detail/modules/updateCacheAlbumPhotosLike.ts new file mode 100644 index 00000000..d5abb5bb --- /dev/null +++ b/src/feature/photo-detail/modules/updateCacheAlbumPhotosLike.ts @@ -0,0 +1,52 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { InfiniteData, QueryClient } from '@tanstack/react-query'; + +interface ToggleAlbumPhotoLikeInCacheParams { + queryClient: QueryClient; + albumId: string; + photoId: number; + isCurrentlyLiked: boolean; +} + +export function updateCacheAlbumPhotosLike({ + queryClient, + albumId, + photoId, + isCurrentlyLiked, +}: ToggleAlbumPhotoLikeInCacheParams) { + queryClient.setQueriesData>( + { queryKey: [EP.album.photos(albumId)] }, + (old) => { + if (!old) return old; + + return { + ...old, + pages: old.pages.map((page) => { + if (!page) return page; + if (!page.responses) return page; + + return { + ...page, + responses: page.responses.map((res) => { + if (res.photoId !== photoId) return res; + + const updated = { ...res }; + + if (res.isLiked !== undefined) { + updated.isLiked = !res.isLiked; + } + + if (res.likeCnt !== undefined) { + updated.likeCnt = isCurrentlyLiked + ? res.likeCnt - 1 + : res.likeCnt + 1; + } + + return updated; + }), + }; + }), + }; + }, + ); +} diff --git a/src/feature/photo-detail/util/calcThumbSwiperCenterOffset.ts b/src/feature/photo-detail/util/calcThumbSwiperCenterOffset.ts new file mode 100644 index 00000000..028b41d4 --- /dev/null +++ b/src/feature/photo-detail/util/calcThumbSwiperCenterOffset.ts @@ -0,0 +1,51 @@ +interface CalcThumbSwiperCenterOffsetProps { + /** + * 현재 뷰포트(또는 Swiper 컨테이너)의 전체 너비 (px 단위) + * - 기본적으로 window.innerWidth를 사용 + * - 반응형 환경에서는 부모 컨테이너 width를 넘겨도 됨 + */ + viewportWidth: number; + + /** + * 활성(선택된) 썸네일의 실제 렌더링 폭 (px 단위) + */ + activeWidth: number; + + /** + * 활성 썸네일의 좌우 margin 값 (px 단위) + */ + activeMargin: number; + + /** + * 비활성 썸네일의 폭 (px 단위) + */ + inactiveWidth: number; + + /** + * 비활성 썸네일의 좌우 margin 값 (px 단위) + */ + inactiveMargin: number; + + /** + * 현재 활성화된 슬라이드의 인덱스 + */ + index: number; +} + +/** preview swiper의 active요소를 화면 중앙에 정렬시키기 위한 translate(오른쪽으로부터 얼마나 떨어져있는지) 계산 */ +export const calcThumbSwiperCenterOffset = ({ + viewportWidth, + activeWidth, + activeMargin, + inactiveWidth, + inactiveMargin, + index, +}: CalcThumbSwiperCenterOffsetProps) => { + // 화면 중앙에서 활성 썸네일의 중심까지 거리 + const baseOffset = viewportWidth / 2 - (activeMargin + activeWidth / 2); + + // 인덱스만큼 비활성 썸네일 폭만큼 왼쪽으로 이동 + const indexOffset = (inactiveWidth + inactiveMargin) * index; + + return baseOffset - indexOffset; +}; diff --git a/src/feature/photo-detail/util/downloadImageFromUrl.ts b/src/feature/photo-detail/util/downloadImageFromUrl.ts new file mode 100644 index 00000000..1faebc3b --- /dev/null +++ b/src/feature/photo-detail/util/downloadImageFromUrl.ts @@ -0,0 +1,23 @@ +export async function downloadImageFromUrl( + imageUrl: string, + fallbackFileName?: string, +) { + // TODO : 사진 fetch 시 CORS발생 수정 필요 + const res = await fetch(imageUrl, { + mode: 'cors', + credentials: 'include', + headers: { Accept: 'image/*' }, + }); + if (!res.ok) throw new Error('이미지 요청 실패'); + + const blob = await res.blob(); + const blobUrl = URL.createObjectURL(blob); + + const a = document.createElement('a'); + a.href = blobUrl; + a.download = fallbackFileName || 'download.jpg'; + document.body.appendChild(a); + a.click(); + document.body.removeChild(a); + URL.revokeObjectURL(blobUrl); +} diff --git a/src/feature/photo-entry/components/AlbumPreviewCard.tsx b/src/feature/photo-entry/components/AlbumPreviewCard.tsx new file mode 100644 index 00000000..392d8c79 --- /dev/null +++ b/src/feature/photo-entry/components/AlbumPreviewCard.tsx @@ -0,0 +1,34 @@ +interface AlbumPreviewCardProps { + imageUrl: string; + nickname: string; + profileUrl: string; +} + +export default function AlbumPreviewCard({ + imageUrl, + nickname, + profileUrl, +}: AlbumPreviewCardProps) { + return ( +
+ 미리보기 이미지 +
+ {`${nickname}님의 + {nickname} +
+
+ ); +} diff --git a/src/feature/photo-entry/components/AlbumSharePreviewSection.tsx b/src/feature/photo-entry/components/AlbumSharePreviewSection.tsx new file mode 100644 index 00000000..7c11bbbd --- /dev/null +++ b/src/feature/photo-entry/components/AlbumSharePreviewSection.tsx @@ -0,0 +1,65 @@ +'use client'; + +import ThreeTags_Fill_Album from '@/../public/assets/album/3Tags_Fill_Album.json'; +import { useGetAlbumInvitation } from '@/feature/album/detail/hooks/useGetAlbumInvitation'; +import { useAlbumPhotosInfiniteQuery } from '@/feature/photo-detail/hooks/useAlbumPhotosInfiniteQuery'; +import MarqueeCarousel from '@/global/components/carousel/MarqueeCarousel'; +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; +import dynamic from 'next/dynamic'; +import AlbumPreviewCard from './AlbumPreviewCard'; +const Lottie = dynamic(() => import('lottie-react'), { ssr: false }); + +interface AlbumSharePreviewSectionProps { + albumId: string; +} + +export default function AlbumSharePreviewSection({ + albumId, +}: AlbumSharePreviewSectionProps) { + const { data, isPending, isError } = useGetAlbumInvitation(albumId); + const { items, isLoading: isItemsLoading } = useAlbumPhotosInfiniteQuery({ + code: albumId, + }); + + if (isPending || isItemsLoading) return null; + if (isError) return null; + if (!data) return null; + + const isEmpty = items.length === 0; + + return ( + <> +
+ + {convertUnicodeToEmoji(data.themeEmoji)} + +
+ +
+

+ {data.title} +

+

{data.eventDate}

+
+ +
+ {isEmpty ? ( + + ) : ( + ( +
+ +
+ ))} + itemWidth={180} + /> + )} +
+ + ); +} diff --git a/src/feature/photo-entry/components/ScreenPhotoShareEntry.tsx b/src/feature/photo-entry/components/ScreenPhotoShareEntry.tsx new file mode 100644 index 00000000..28310874 --- /dev/null +++ b/src/feature/photo-entry/components/ScreenPhotoShareEntry.tsx @@ -0,0 +1,92 @@ +'use client'; +import { useAlbumEnterMutation } from '@/feature/album-entry/hooks/useAlbumEnterMutation'; +import { useGetAlbumAvailableCount } from '@/feature/album/detail/hooks/useGetAlbumAvailableCount'; +import { handleFileUpload } from '@/feature/create-album/utils/handleFileUpload'; +import CheckNoImgModal from '@/feature/upload/components/CheckNoImgModal'; +import CustomHeader from '@/global/components/header/CustomHeader'; +import LongButton from '@/global/components/LongButton'; +import Toast from '@/global/components/toast/Toast'; +import BubbleTooltip from '@/global/components/tooltip/BubbleTooltip'; +import { useRouter, useSearchParams } from 'next/navigation'; +import { useEffect, useRef } from 'react'; +import AlbumSharePreviewSection from './AlbumSharePreviewSection'; + +interface ScreenPhotoShareEntryProps { + albumId: string; +} + +export default function ScreenPhotoShareEntry({ + albumId, +}: ScreenPhotoShareEntryProps) { + const router = useRouter(); + const searchParams = useSearchParams(); + const isInvite = searchParams.get('isInvite'); + const fileInputRef = useRef(null); + + const { mutateAsync } = useAlbumEnterMutation(); + const { data } = useGetAlbumAvailableCount(albumId); + + useEffect(() => { + if (!isInvite) return; + + mutateAsync({ albumId }); + }, []); + + const handleUpload = () => { + fileInputRef.current?.click(); + }; + + const onFileChange = async (e: React.ChangeEvent) => { + const { success } = await handleFileUpload(e, albumId, router); + + if (success) { + setTimeout(() => Toast.check(`총 ${success}장을 앨범에 채웠어요.`), 2000); + } + }; + + return ( + <> + + +
+ + +
+
+ {data?.availableCount && ( + + )} + + +
+ + + 올릴 사진이 없어요 + + } + /> +
+
+ + ); +} diff --git a/src/feature/root/components/RendingFooter.tsx b/src/feature/root/components/RendingFooter.tsx new file mode 100644 index 00000000..4dc8f805 --- /dev/null +++ b/src/feature/root/components/RendingFooter.tsx @@ -0,0 +1,26 @@ +import Link from 'next/link'; + +export default function RendingFooter() { + return ( +
+ 치이이즈 + + @치이이즈. ALL RIGHTS RESERVED + + + 버그 및 불편사항 제보 + + + 개인정보처리방침 | 서비스 이용약관 + +
+ ); +} diff --git a/src/feature/root/components/ScreenRoot.tsx b/src/feature/root/components/ScreenRoot.tsx new file mode 100644 index 00000000..645133c4 --- /dev/null +++ b/src/feature/root/components/ScreenRoot.tsx @@ -0,0 +1,139 @@ +'use client'; +import LogoHeader from '@/global/components/header/LogoHeader'; +import LongButton from '@/global/components/LongButton'; +import { useCheckAuth } from '@/global/hooks/useCheckAuth'; +import dynamic from 'next/dynamic'; +import Image from 'next/image'; +import { useRouter } from 'next/navigation'; +import { useCallback, useState } from 'react'; +import SelectMenu from './SelectMenu'; + +const RendingFooter = dynamic(() => import('./RendingFooter'), { ssr: false }); +const SelectedList = dynamic(() => import('./SelectedList'), { ssr: false }); +const SwipeList = dynamic(() => import('./SwipeList'), { ssr: false }); + +export default function ScreenRoot() { + const router = useRouter(); + const [selectedMenu, setSelectedMenu] = useState< + 'first' | 'second' | 'third' + >('first'); + const handleCreateAlbumClick = useCallback(() => { + if (typeof document !== 'undefined') { + document.cookie = 'entry=create-album; path=/;'; + } + router.push('/login'); + }, [router]); + + useCheckAuth({ onAuthed: () => router.push('/main') }); + + return ( +
+ + + 딱 7일만 열리는 특별한
+ 공유 앨범 서비스 +
+ +
+ 블러 배경 + 핸드폰 일러스트 +
+ + + 92개 모임에서 치이이즈를 사용했어요 + + + + 삼각형 + 소개 텍스트 + 치즈 아이콘 + 치즈 아이콘 + + 일정의 마무리, +
+ 사진 주고받기도 가볍고 귀엽게 +
+
+ 추억을 더 소중하게 만드는 + 치이이즈의 세 가지 방법 +
+
+ +
+
+ +
+
+ 서비스에 그대로 담긴 + 사진 찍고 나눌 때의 감정 +
+ 박스 일러스트 +
+ 추억할 일이 있는 모든 곳에서 + 모두 함께 치이이즈 +
+ + +
+ ); +} diff --git a/src/feature/root/components/SelectMenu.tsx b/src/feature/root/components/SelectMenu.tsx new file mode 100644 index 00000000..2913de44 --- /dev/null +++ b/src/feature/root/components/SelectMenu.tsx @@ -0,0 +1,64 @@ +'use client'; + +interface MenuItemProps { + title: string; + selected?: boolean; + onClick?: () => void; +} + +function MenuItem({ title, selected, onClick }: MenuItemProps) { + return ( +
+ {title} +
+ ); +} + +const MENU_TITLES = ['7일', '띱', '치즈네컷']; + +export interface SelectMenuProps { + selectedMenu?: 'first' | 'second' | 'third'; + setSelectedMenu?: (menu: 'first' | 'second' | 'third') => void; +} + +export default function SelectMenu({ + selectedMenu = 'first', // 기본 first (1번) + setSelectedMenu, +}: SelectMenuProps) { + const selectedIdx = + selectedMenu === 'first' + ? 0 + : selectedMenu === 'second' + ? 1 + : selectedMenu === 'third' + ? 2 + : 0; // 기본 0 + + const handleClick = (idx: number) => { + if (!setSelectedMenu) return; + if (idx === 0) setSelectedMenu('first'); + else if (idx === 1) setSelectedMenu('second'); + else if (idx === 2) setSelectedMenu('third'); + }; + + return ( +
+ {MENU_TITLES.map((title, idx) => ( + handleClick(idx)} + /> + ))} +
+ ); +} diff --git a/src/feature/root/components/SelectedList.tsx b/src/feature/root/components/SelectedList.tsx new file mode 100644 index 00000000..b0fb19d2 --- /dev/null +++ b/src/feature/root/components/SelectedList.tsx @@ -0,0 +1,147 @@ +'use client'; + +function debounce( + func: (...args: T) => void, + wait: number, +) { + let timeout: ReturnType; + return (...args: T) => { + clearTimeout(timeout); + timeout = setTimeout(() => func(...args), wait); + }; +} + +import Image from 'next/image'; +import { useEffect, useRef } from 'react'; + +interface SelectedListProps { + selectedMenu: 'first' | 'second' | 'third'; + setSelectedMenu: (menu: 'first' | 'second' | 'third') => void; +} + +const MENU_IMAGES = [ + { key: 'first', src: '/assets/rending/first.svg', alt: '7일 설명' }, + { key: 'second', src: '/assets/rending/second.svg', alt: '띱 설명' }, + { key: 'third', src: '/assets/rending/third.svg', alt: '치즈네컷 설명' }, +]; + +export default function SelectedList({ + selectedMenu, + setSelectedMenu, +}: SelectedListProps) { + const selectedIdx = + selectedMenu === 'first' + ? 0 + : selectedMenu === 'second' + ? 1 + : selectedMenu === 'third' + ? 2 + : 0; + + const containerRef = useRef(null); + const isScrollingProgrammatically = useRef(false); + const ITEM_WIDTH = '100%'; // 각 아이템의 너비 (부모 컨테이너에서 좌우 패딩 및 간격 제외한 값) + + // 스크롤 시 중앙에 가장 가까운 이미지의 index 계산 및 상태 변경 (debounce 적용) + + const handleScrollCore = () => { + if (!containerRef.current) return; + if (isScrollingProgrammatically.current) return; + const containerRect = containerRef.current.getBoundingClientRect(); + const containerCenter = containerRect.left + containerRect.width / 2; + const children = containerRef.current.children; + + let closestIndex = 0; + let minDistance = Infinity; + + for (let i = 0; i < children.length; i++) { + const childRect = children[i].getBoundingClientRect(); + const childCenter = childRect.left + childRect.width / 2; + const distance = Math.abs(childCenter - containerCenter); + if (distance < minDistance) { + minDistance = distance; + closestIndex = i; + } + } + + const newMenu = MENU_IMAGES[closestIndex].key as + | 'first' + | 'second' + | 'third'; + if (newMenu !== selectedMenu) { + setSelectedMenu(newMenu); + } + }; + + const handleScroll = debounce(handleScrollCore, 50); + + useEffect(() => { + if (!containerRef.current) return; + + const container = containerRef.current; + const children = container.children; + + if (children[selectedIdx]) { + const targetChild = children[selectedIdx] as HTMLElement; + const containerRect = container.getBoundingClientRect(); + const targetRect = targetChild.getBoundingClientRect(); + + const scrollLeft = + container.scrollLeft + + (targetRect.left - containerRect.left) - + (containerRect.width / 2 - targetRect.width / 2); + + isScrollingProgrammatically.current = true; + container.scrollTo({ left: scrollLeft, behavior: 'smooth' }); + + setTimeout(() => { + isScrollingProgrammatically.current = false; + }, 500); + } + }, [selectedIdx]); + + return ( +
+
+ {MENU_IMAGES.map((image) => ( +
+ {image.alt} +
+ ))} +
+
+ {MENU_IMAGES.map((image, idx) => ( +
+ setSelectedMenu(image.key as 'first' | 'second' | 'third') + } + /> + ))} +
+
+ ); +} diff --git a/src/feature/root/components/SwipeList.tsx b/src/feature/root/components/SwipeList.tsx new file mode 100644 index 00000000..a79d2d09 --- /dev/null +++ b/src/feature/root/components/SwipeList.tsx @@ -0,0 +1,30 @@ +import MarqueeCarousel from '@/global/components/carousel/MarqueeCarousel'; +import Image from 'next/image'; + +const SWIPE_IMAGES = [ + '/assets/rending/swipe/1.png', + '/assets/rending/swipe/2.png', + '/assets/rending/swipe/3.png', + '/assets/rending/swipe/4.png', +]; + +export default function SwipeList() { + return ( + ( + {`스와이프 + ))} + itemWidth={163} + gap={0} + speed={60} + className='py-2' + /> + ); +} diff --git a/src/feature/term/ScreenTerm.tsx b/src/feature/term/ScreenTerm.tsx new file mode 100644 index 00000000..411de989 --- /dev/null +++ b/src/feature/term/ScreenTerm.tsx @@ -0,0 +1,30 @@ +'use client'; +import { TermContent } from '@/feature/onboarding/components/TermContent'; +import CustomHeader from '@/global/components/header/CustomHeader'; +import { useSearchParams } from 'next/navigation'; + +export default function ScreenTerm() { + const searchParams = useSearchParams(); + const termType = searchParams.get('type'); + const currentTerm = + termType && Object.prototype.hasOwnProperty.call(TermContent, termType) + ? TermContent[termType as keyof typeof TermContent] + : null; + + if (!currentTerm) { + return ( +
+ 존재하지 않는 약관입니다. +
+ ); + } + + return ( +
+ +
+ +
+
+ ); +} diff --git a/src/feature/upload/api/getAlbumInvitation.server.ts b/src/feature/upload/api/getAlbumInvitation.server.ts new file mode 100644 index 00000000..e3a635ee --- /dev/null +++ b/src/feature/upload/api/getAlbumInvitation.server.ts @@ -0,0 +1,16 @@ +import { serverApi } from '@/global/utils/serverApi'; +import type { AlbumInvitationResponse } from './getAlbumInvitation'; + +/** + * 앨범 초대 정보 조회 (Server Component용) + * @param code 앨범 코드 + * @returns 앨범 초대 정보 + */ +export async function getAlbumInvitationServer( + code: string, +): Promise { + const response = await serverApi.get({ + path: `/v1/album/${code}/invitation`, + }); + return response.result; +} diff --git a/src/feature/upload/api/getAlbumInvitation.ts b/src/feature/upload/api/getAlbumInvitation.ts new file mode 100644 index 00000000..23b3e1f6 --- /dev/null +++ b/src/feature/upload/api/getAlbumInvitation.ts @@ -0,0 +1,25 @@ +import { api } from '@/global/utils/api'; + +export type AlbumInvitationResponse = { + title: string; + themeEmoji: string; + eventDate: string; + expiredAt: string; + makerName: string; + makerProfileImage: string; + isExpired: boolean; +}; + +/** + * 앨범 초대 정보 조회 (Client Component용) + * @param code 앨범 코드 + * @returns 앨범 초대 정보 + */ +export async function getAlbumInvitation( + code: string, +): Promise { + const response = await api.get({ + path: `/v1/album/${code}/invitation`, + }); + return response.result; +} diff --git a/src/feature/upload/components/AlbumInfoHeader.tsx b/src/feature/upload/components/AlbumInfoHeader.tsx new file mode 100644 index 00000000..f9fff0a4 --- /dev/null +++ b/src/feature/upload/components/AlbumInfoHeader.tsx @@ -0,0 +1,61 @@ +import { convertUnicodeToEmoji } from '@/global/utils/convertEmoji'; + +export interface AlbumParticipant { + name?: string; + profileImage?: string; + role?: string; // "MAKER" | "GUEST" 등으로 좁힐 수 있음 + isMe?: boolean; +} + +export interface AlbumInvitation { + currentParticipantCount?: number; + eventDate?: string; + expiredAt?: string; + isExpired?: boolean; + maxParticipantCount?: number; + myRole?: string; + participants?: AlbumParticipant[]; + themeEmoji?: string; + title?: string; +} + +type AlbumInfoHeaderProps = { + photoCount: number; + albumData: AlbumInvitation; +}; + +export default function AlbumInfoHeader({ + photoCount, + albumData, +}: AlbumInfoHeaderProps) { + const emoji = convertUnicodeToEmoji(albumData.themeEmoji || ''); + + return ( +
+
+ {emoji} +
+ +
+

+ {albumData.title || '제목 없음'} +

+ {photoCount === 0 ? ( +

+ 앨범을 채워주세요 +

+ ) : ( +

+ {albumData.eventDate || '날짜 없음'} +

+ )} +
+
+ ); +} diff --git a/src/feature/upload/components/AvailableCountBubble.tsx b/src/feature/upload/components/AvailableCountBubble.tsx new file mode 100644 index 00000000..636ee8d5 --- /dev/null +++ b/src/feature/upload/components/AvailableCountBubble.tsx @@ -0,0 +1,23 @@ +interface AvailableCountBubbleProps { + availableCount: number; +} + +export default function AvailableCountBubble({ + availableCount, +}: AvailableCountBubbleProps) { + return ( +
+
+
+ + 📸 + + 지금 {availableCount}장 더 올릴 수 있어요 +
+
+
+
+
+
+ ); +} diff --git a/src/feature/upload/components/CheckNoImgModal.tsx b/src/feature/upload/components/CheckNoImgModal.tsx new file mode 100644 index 00000000..571f02b6 --- /dev/null +++ b/src/feature/upload/components/CheckNoImgModal.tsx @@ -0,0 +1,67 @@ +'use client'; + +import { handleFileUpload } from '@/feature/create-album/utils/handleFileUpload'; +import ConfirmModal from '@/global/components/modal/ConfirmModal'; +import Toast from '@/global/components/toast/Toast'; +import { useRouter } from 'next/navigation'; +import { ReactNode, useRef } from 'react'; + +interface CheckNoImgModalProps { + trigger: ReactNode; + albumId: string; + onConfirm?: () => void; +} + +export default function CheckNoImgModal({ + trigger, + albumId, + onConfirm, +}: CheckNoImgModalProps) { + const router = useRouter(); + const fileInputRef = useRef(null); + + const handleCancel = () => { + fileInputRef.current?.click(); + }; + + const handleConfirm = () => { + if (onConfirm) { + onConfirm(); + return; + } + // 기본 동작: 현재 앨범 경로로 이동 (WaitingAlbum에서 분기 처리) + router.push(`/album/detail/${albumId}`); + }; + + const handleFileChange = async (e: React.ChangeEvent) => { + try { + await handleFileUpload(e, albumId, router); + } catch (e) { + console.error(e); + Toast.alert('사진 업로드 중 오류가 발생했습니다.'); + } + }; + + return ( + <> + + + + ); +} diff --git a/src/feature/upload/components/UploadAlbumPage.tsx b/src/feature/upload/components/UploadAlbumPage.tsx new file mode 100644 index 00000000..67004824 --- /dev/null +++ b/src/feature/upload/components/UploadAlbumPage.tsx @@ -0,0 +1,71 @@ +'use client'; +import LotateAnimation from '@/../public/assets/upload/3Tags_Fill Album.json'; +import CheckNoImgModal from '@/feature/upload/components/CheckNoImgModal'; +import CustomHeader from '@/global/components/header/CustomHeader'; +import dynamic from 'next/dynamic'; +import { useGetAlbumInform } from '../hooks/useGetAlbumInform'; +import AlbumInfoHeader from './AlbumInfoHeader'; +import AvailableCountBubble from './AvailableCountBubble'; +import UploadButton from './UploadButton'; + +const Lottie = dynamic(() => import('lottie-react'), { ssr: false }); + +interface AlbumCard { + imageUrl: string; + nickname: string; + profileUrl: string; +} + +// ✅ API 연동 전까지는 상수로 유지 +const MOCK_CARDS: AlbumCard[] = []; + +interface UploadAlbumPageProps { + albumId: string; +} + +export default function UploadAlbumPage({ albumId }: UploadAlbumPageProps) { + const { data } = useGetAlbumInform({ code: albumId }); + const cards = MOCK_CARDS; // ✅ 나중에 API 결과로 교체 예정 + const availableCount = + (data?.maxParticipantCount ?? 0) - (data?.currentParticipantCount ?? 0); + return ( +
+ +
+
+ {data && ( + + )} + +
+ +
+ {data?.myRole !== 'MAKER' ? ( + + ) : ( + + Tip. 첫 업로드가 참여도를 두 배 넘게 끌려올려요 + + )} + + + 올릴 사진이 없어요 + + } + /> +
+
+
+ ); +} diff --git a/src/feature/upload/components/UploadButton.tsx b/src/feature/upload/components/UploadButton.tsx new file mode 100644 index 00000000..18426639 --- /dev/null +++ b/src/feature/upload/components/UploadButton.tsx @@ -0,0 +1,43 @@ +'use client'; + +import { handleFileUpload } from '@/feature/create-album/utils/handleFileUpload'; +import LongButton from '@/global/components/LongButton'; +import { useRouter } from 'next/navigation'; +import { useRef } from 'react'; + +type UploadButtonProps = { + albumId: string; +}; + +export default function UploadButton({ albumId }: UploadButtonProps) { + const router = useRouter(); + const fileInputRef = useRef(null); + + function handleButtonClick() { + fileInputRef.current?.click(); + } + + async function onFileChange(e: React.ChangeEvent) { + await handleFileUpload(e, albumId, router); + } + + return ( + <> + +
+ +
+ + ); +} diff --git a/src/feature/upload/hooks/useGetAlbumInform.server.ts b/src/feature/upload/hooks/useGetAlbumInform.server.ts new file mode 100644 index 00000000..082784ba --- /dev/null +++ b/src/feature/upload/hooks/useGetAlbumInform.server.ts @@ -0,0 +1,9 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { serverApi } from '@/global/utils/serverApi'; + +export async function getAlbumDataWithRoleServer(code: string) { + const data = await serverApi.get({ + path: EP.album.participants(code), + }); + return data.result; +} diff --git a/src/feature/upload/hooks/useGetAlbumInform.ts b/src/feature/upload/hooks/useGetAlbumInform.ts new file mode 100644 index 00000000..9ceca3b8 --- /dev/null +++ b/src/feature/upload/hooks/useGetAlbumInform.ts @@ -0,0 +1,22 @@ +import { ApiReturns, EP } from '@/global/api/ep'; +import { api } from '@/global/utils/api'; +import { useQuery } from '@tanstack/react-query'; + +const getAlbumDataWithRole = async (code: string) => { + const data = await api.get({ + path: EP.album.participants(code), + }); + return data.result; +}; + +interface UseGetAlbumInformProps { + code: string; +} + +export function useGetAlbumInform({ code }: UseGetAlbumInformProps) { + return useQuery({ + queryKey: [EP.album.participants(code)], + queryFn: () => getAlbumDataWithRole(code), + enabled: !!code, + }); +} diff --git a/src/global/api/ep.ts b/src/global/api/ep.ts new file mode 100644 index 00000000..cb924fdb --- /dev/null +++ b/src/global/api/ep.ts @@ -0,0 +1,204 @@ +/* AUTO-GENERATED FILE. DO NOT EDIT. + * Generated by scripts/generate-ep.ts + */ + +export const EP = { + global: { + "health": () => `/v1/global/health-check`, + }, + auth: { + "exchange": () => `/v1/auth/exchange`, + "logout": () => `/v1/auth/logout`, + "reissue": () => `/v1/auth/reissue`, + }, + user: { + "userMe": () => `/v1/user/me`, + "userMeName": () => `/v1/user/me/name`, + "userMeProfileImage": () => `/v1/user/me/profile-image`, + "userOnboarding": () => `/v1/user/onboarding`, + "userProfileImages": () => `/v1/user/profile-images`, + }, + album: { + "create": () => `/v1/album`, + "availableCount": (code: string) => `/v1/album/${code}/available-count`, + "albumBest-4cut": (code: string) => `/v1/album/${code}/best-4cut`, + "enter": (code: string) => `/v1/album/${code}/enter`, + "albumInfo": (code: string) => `/v1/album/${code}/info`, + "invitation": (code: string) => `/v1/album/${code}/invitation`, + "participants": (code: string) => `/v1/album/${code}/participants`, + "albumParticipantsMe": (code: string) => `/v1/album/${code}/participants/me`, + "albumPhoto": (code: string, photoId: number) => `/v1/album/${code}/photo/${photoId}`, + "photos": (code: string) => `/v1/album/${code}/photos`, + "photoDetail": (code: string, photoId: number) => `/v1/album/${code}/photos/${photoId}`, + "albumPhotosLikers": (code: string, photoId: number) => `/v1/album/${code}/photos/${photoId}/likers`, + "likedPhotos": (code: string) => `/v1/album/${code}/photos/liked`, + "albumClosed": () => `/v1/album/closed`, + "albumOpen": () => `/v1/album/open`, + "albumOpenMe": () => `/v1/album/open/me`, + }, + photo: { + "like": (photoId: number) => `/v1/photo/${photoId}/liked`, + "unlike": (photoId: number) => `/v1/photo/${photoId}/unliked`, + "presignedDownload": () => `/v1/photo/download-url`, + "presignedUpload": () => `/v1/photo/presigned-url`, + "reportUploadResult": () => `/v1/photo/report`, + }, + cheese4cut: { + "finalize": (code: string) => `/v1/cheese4cut/${code}/fixed`, + "preview": (code: string) => `/v1/cheese4cut/${code}/preview`, + }, + internal: { + "thumbnailComplete": () => `/internal/thumbnail/complete`, + } +} as const; + +// 선택지 Enum(정렬) +export type PhotoSorting = 'POPULAR' | 'CAPTURED_AT' | 'CREATED_AT'; + +/* ======================= + * Generated Types + * ======================= */ + +// --- Components +export interface UserOnboardingRequestSchema { "name": string; "imageCode": string; "isServiceAgreement": boolean; "isUserInfoAgreement": boolean; "isMarketingAgreement"?: boolean; "isThirdPartyAgreement": boolean; } +export interface CommonResponseVoidSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: unknown; } +export interface PhotoUploadReportRequestSchema { "failurePhotoIds": number[]; } +export interface FileInfoSchema { "fileName": string; "captureTime": string; "fileSize"?: number; "contentType": string; } +export interface PhotoPresignedUrlRequestSchema { "albumCode": string; "fileInfos": FileInfoSchema[]; } +export interface CommonResponsePhotoPresignedUrlResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: PhotoPresignedUrlResponseSchema; } +export interface PhotoPresignedUrlResponseSchema { "presignedUrlInfos": PresignedUrlInfoSchema[]; } +export interface PresignedUrlInfoSchema { "photoId": number; "uploadUrl": string; } +export interface PhotoDownloadRequestSchema { "code": string; "photoIds": number[]; } +export interface CommonResponsePhotoDownloadResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: PhotoDownloadResponseSchema; } +export interface DownloadFileInfoSchema { "photoId": number; "downloadUrl": string; "fileName": string; "captureTime": string; "createdAt": string; } +export interface PhotoDownloadResponseSchema { "downloadFiles": DownloadFileInfoSchema[]; } +export interface Cheese4cutFixedRequestSchema { "photoIds": number[]; } +export interface AuthReissueRequestSchema { "refreshToken": string; } +export interface AuthReissueResponseSchema { "accessToken": string; "refreshToken": string; } +export interface CommonResponseAuthReissueResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: AuthReissueResponseSchema; } +export interface AlbumCreationRequestSchema { "themeEmoji": string; "title": string; "participant": number; "eventDate": string; } +export interface AlbumCreationResponseSchema { "themeEmoji": string; "title": string; "eventDate": string; "currentPhotoCnt": number; "code": string; } +export interface CommonResponseAlbumCreationResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: AlbumCreationResponseSchema; } +export type AlbumEnterResponseSchema = unknown; +export interface AlbumMakerInfoSchema { "makerName": string; "makerProfileImage": string; } +export interface CommonResponseAlbumEnterResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: ExistingEnterResponseSchema | NewEnterResponseSchema; } +export type ExistingEnterResponseSchema = AlbumEnterResponseSchema & { "joinStatus"?: "NEW" | "EXISTING" | "REJOINED"; "title"?: string; "themeEmoji"?: string; "eventDate"?: string; "expiredAt"?: string; "makerInfo"?: AlbumMakerInfoSchema; }; +export type NewEnterResponseSchema = AlbumEnterResponseSchema & { "joinStatus"?: "NEW" | "EXISTING" | "REJOINED"; "title"?: string; "themeEmoji"?: string; "eventDate"?: string; "expiredAt"?: string; "makerInfo"?: AlbumMakerInfoSchema; "remainingUploadSlots"?: number; "recentPhotos"?: RecentPhotoResponseSchema; }; +export interface RecentPhotoResponseSchema { "thumbnailUrl": string; "uploaderName": string; "uploaderProfileImage": string; } +export interface PhotoCompleteRequestSchema { "photoId": number; "thumbnailUrl": string; } +export interface UserProfileImageRequestSchema { "imageCode"?: string; } +export interface UserProfileRequestSchema { "name"?: string; } +export interface CommonResponseUserProfileImageResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: UserProfileImageResponseSchema; } +export interface ProfileImageOptSchema { "imageCode"?: string; "profileImageUrl"?: string; } +export interface UserProfileImageResponseSchema { "opts"?: ProfileImageOptSchema[]; } +export interface CommonResponseUserInfoResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: UserInfoResponseSchema; } +export interface UserInfoResponseSchema { "profileImage": string; "email": string; "name": string; "albumCount": number; "photoCount": number; "likesCount": number; } +export interface CommonResponseStringSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: string; } +export type Cheese4cutFinalResponseSchema = Cheese4cutResponseSchema & { "isFinalized"?: boolean; "photos"?: FinalPhotoInfoSchema[]; }; +export type Cheese4cutPreviewResponseSchema = Cheese4cutResponseSchema & { "isFinalized"?: boolean; "previewPhotos"?: PreviewPhotoInfoSchema[]; "uniqueLikesCount"?: number; "participant"?: number; "myRole"?: "MAKER" | "GUEST" | "BLACK"; }; +export interface Cheese4cutResponseSchema { "finalized"?: boolean; } +export interface CommonResponseCheese4cutResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: Cheese4cutFinalResponseSchema | Cheese4cutPreviewResponseSchema; } +export interface FinalPhotoInfoSchema { "photoId": number; "imageUrl": string; "photoRank": number; } +export interface PreviewPhotoInfoSchema { "photoId": number; "imageUrl": string; "photoRank": number; } +export interface AuthExchangeResponseSchema { "accessToken": string; "refreshToken": string; "isOnboarded": boolean; "userId": number; "name": string; "email": string; } +export interface CommonResponseAuthExchangeResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: AuthExchangeResponseSchema; } +export interface CommonResponsePhotoPageResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: PhotoPageResponseSchema; } +export interface PhotoListResponseSchema { "name"?: string; "photoId": number; "profileImage": string; "imageUrl"?: string; "thumbnailUrl": string; "likeCnt": number; "isLiked": boolean; "isDownloaded": boolean; "isRecentlyDownloaded": boolean; } +export interface PhotoPageResponseSchema { "responses": PhotoListResponseSchema[]; "listSize": number; "isFirst": boolean; "isLast": boolean; "hasNext": boolean; } +export interface CommonResponsePhotoDetailResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: PhotoDetailResponseSchema; } +export interface PhotoDetailResponseSchema { "name": string; "profileImage": string; "photoId": number; "imageUrl": string; "thumbnailUrl": string; "likesCnt": number; "isLiked": boolean; "isDownloaded": boolean; "isRecentlyDownloaded": boolean; "canDelete"?: boolean; "captureTime"?: string; "createdAt"?: string; } +export interface CommonResponsePhotoLikedUserResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: PhotoLikedUserResponseSchema; } +export interface PhotoLikedUserResponseSchema { "likeCnt"?: number; "photoLikers"?: PhotoLikerSchema[]; } +export interface PhotoLikerSchema { "name"?: string; "profileImageUrl"?: string; "isMe"?: boolean; "role"?: "MAKER" | "GUEST" | "BLACK"; } +export interface CommonResponsePhotoLikedPageResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: PhotoLikedPageResponseSchema; } +export interface PhotoLikedPageResponseSchema { "responses": PhotoLikedResponseSchema[]; "listSize": number; "isFirst": boolean; "isLast": boolean; "hasNext": boolean; } +export interface PhotoLikedResponseSchema { "name"?: string; "photoId": number; "imageUrl"?: string; "thumbnailUrl": string; "likeCnt"?: number; "isLiked"?: boolean; "isDownloaded": boolean; "isRecentlyDownloaded": boolean; } +export interface AlbumParticipantResponseSchema { "isExpired": boolean; "title": string; "themeEmoji": string; "eventDate": string; "expiredAt": string; "maxParticipantCount": number; "currentParticipantCount": number; "participants": ParticipantInfoSchema[]; "myRole"?: "MAKER" | "GUEST" | "BLACK"; } +export interface CommonResponseAlbumParticipantResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: AlbumParticipantResponseSchema; } +export interface ParticipantInfoSchema { "name": string; "profileImage": string; "role": "MAKER" | "GUEST" | "BLACK"; "isMe": boolean; } +export interface AlbumInvitationResponseSchema { "title": string; "themeEmoji": string; "eventDate": string; "expiredAt": string; "makerName": string; "makerProfileImage": string; "isExpired": boolean; } +export interface CommonResponseAlbumInvitationResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: AlbumInvitationResponseSchema; } +export interface AlbumInfoResponseSchema { "makerId"?: number; "name"?: string; "title"?: string; "themeEmoji"?: string; "participant"?: number; "currentParticipant"?: number; "eventDate"?: string; "currentPhotoCnt"?: number; "expiredAt"?: string; } +export interface CommonResponseAlbumInfoResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: AlbumInfoResponseSchema; } +export interface AlbumBest4CutResponseSchema { "thumbnailUrl"?: string; "likeCnt"?: number; "isLiked"?: boolean; } +export interface CommonResponseListAlbumBest4CutResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: AlbumBest4CutResponseSchema[]; } +export interface CommonResponseUploadAvailableCountResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: UploadAvailableCountResponseSchema; } +export interface UploadAvailableCountResponseSchema { "availableCount": number; "maxPhotoCount": number; "currentPhotoCount": number; } +export interface CommonResponseOpenAlbumPageResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: OpenAlbumPageResponseSchema; } +export interface OpenAlbumPageResponseSchema { "responses": OpenAlbumSummaryResponseSchema[]; "listSize": number; "isFirst": boolean; "isLast": boolean; "hasNext": boolean; } +export interface OpenAlbumSummaryResponseSchema { "code": string; "themeEmoji": string; "title": string; "eventDate": string; "makerName": string; "currentParticipant": number; "participant": number; "expiredAt": string; "recentPhotoThumbnails"?: string[]; } +export interface ClosedAlbumPageResponseSchema { "responses": ClosedAlbumSummaryResponseSchema[]; "listSize": number; "isFirst": boolean; "isLast": boolean; "hasNext": boolean; } +export interface ClosedAlbumSummaryResponseSchema { "code": string; "title": string; "makerName": string; "eventDate": string; "thumbnails"?: string[]; } +export interface CommonResponseClosedAlbumPageResponseSchema { "isSuccess"?: boolean; "code"?: number; "message"?: string; "result"?: ClosedAlbumPageResponseSchema; } + +// --- Operation Response Types +export type GlobalHealthResponse = CommonResponseStringSchema["result"]; +export type AuthExchangeResponse = CommonResponseAuthExchangeResponseSchema["result"]; +export type AuthLogoutResponse = CommonResponseVoidSchema["result"]; +export type AuthReissueResponse = CommonResponseAuthReissueResponseSchema["result"]; +export type UserUserMeResponse = CommonResponseUserInfoResponseSchema["result"]; +export type UserUserMeNameResponse = CommonResponseVoidSchema["result"]; +export type UserUserMeProfileImageResponse = CommonResponseVoidSchema["result"]; +export type UserUserOnboardingResponse = CommonResponseVoidSchema["result"]; +export type UserUserProfileImagesResponse = CommonResponseUserProfileImageResponseSchema["result"]; +export type AlbumCreateResponse = CommonResponseAlbumCreationResponseSchema["result"]; +export type AlbumAvailableCountResponse = CommonResponseUploadAvailableCountResponseSchema["result"]; +export type AlbumAlbumBest4cutResponse = CommonResponseListAlbumBest4CutResponseSchema["result"]; +export type AlbumEnterResponse = CommonResponseAlbumEnterResponseSchema["result"]; +export type AlbumAlbumInfoResponse = CommonResponseAlbumInfoResponseSchema["result"]; +export type AlbumInvitationResponse = CommonResponseAlbumInvitationResponseSchema["result"]; +export type AlbumParticipantsResponse = CommonResponseAlbumParticipantResponseSchema["result"]; +export type AlbumAlbumParticipantsMeResponse = CommonResponseVoidSchema["result"]; +export type AlbumAlbumPhotoResponse = CommonResponseVoidSchema["result"]; +export type AlbumPhotosResponse = CommonResponsePhotoPageResponseSchema["result"]; +export type AlbumPhotoDetailResponse = CommonResponsePhotoDetailResponseSchema["result"]; +export type AlbumAlbumPhotosLikersResponse = CommonResponsePhotoLikedUserResponseSchema["result"]; +export type AlbumLikedPhotosResponse = CommonResponsePhotoLikedPageResponseSchema["result"]; +export type AlbumAlbumClosedResponse = CommonResponseClosedAlbumPageResponseSchema["result"]; +export type AlbumAlbumOpenResponse = CommonResponseOpenAlbumPageResponseSchema["result"]; +export type AlbumAlbumOpenMeResponse = CommonResponseOpenAlbumPageResponseSchema["result"]; +export type PhotoLikeResponse = CommonResponseVoidSchema["result"]; +export type PhotoUnlikeResponse = CommonResponseVoidSchema["result"]; +export type PhotoPresignedDownloadResponse = CommonResponsePhotoDownloadResponseSchema["result"]; +export type PhotoPresignedUploadResponse = CommonResponsePhotoPresignedUrlResponseSchema["result"]; +export type PhotoReportUploadResultResponse = CommonResponseVoidSchema["result"]; +export type Cheese4cutFinalizeResponse = CommonResponseVoidSchema["result"]; +export type Cheese4cutPreviewResponse = CommonResponseCheese4cutResponseSchema["result"]; +export type InternalThumbnailCompleteResponse = CommonResponseVoidSchema["result"]; + +// --- Mapping: 'group.fn' -> Response Type +export interface ApiReturns { + "global.health": GlobalHealthResponse; // GET /v1/global/health-check + "auth.exchange": AuthExchangeResponse; // GET /v1/auth/exchange + "auth.logout": AuthLogoutResponse; // POST /v1/auth/logout + "auth.reissue": AuthReissueResponse; // POST /v1/auth/reissue + "user.userMe": UserUserMeResponse; // GET /v1/user/me + "user.userMeName": UserUserMeNameResponse; // PATCH /v1/user/me/name + "user.userMeProfileImage": UserUserMeProfileImageResponse; // PATCH /v1/user/me/profile-image + "user.userOnboarding": UserUserOnboardingResponse; // POST /v1/user/onboarding + "user.userProfileImages": UserUserProfileImagesResponse; // GET /v1/user/profile-images + "album.create": AlbumCreateResponse; // POST /v1/album + "album.availableCount": AlbumAvailableCountResponse; // GET /v1/album/{code}/available-count + "album.albumBest-4cut": AlbumAlbumBest4cutResponse; // GET /v1/album/{code}/best-4cut + "album.enter": AlbumEnterResponse; // POST /v1/album/{code}/enter + "album.albumInfo": AlbumAlbumInfoResponse; // GET /v1/album/{code}/info + "album.invitation": AlbumInvitationResponse; // GET /v1/album/{code}/invitation + "album.participants": AlbumParticipantsResponse; // GET /v1/album/{code}/participants + "album.albumParticipantsMe": AlbumAlbumParticipantsMeResponse; // DELETE /v1/album/{code}/participants/me + "album.albumPhoto": AlbumAlbumPhotoResponse; // DELETE /v1/album/{code}/photo/{photoId} + "album.photos": AlbumPhotosResponse; // GET /v1/album/{code}/photos + "album.photoDetail": AlbumPhotoDetailResponse; // GET /v1/album/{code}/photos/{photoId} + "album.albumPhotosLikers": AlbumAlbumPhotosLikersResponse; // GET /v1/album/{code}/photos/{photoId}/likers + "album.likedPhotos": AlbumLikedPhotosResponse; // GET /v1/album/{code}/photos/liked + "album.albumClosed": AlbumAlbumClosedResponse; // GET /v1/album/closed + "album.albumOpen": AlbumAlbumOpenResponse; // GET /v1/album/open + "album.albumOpenMe": AlbumAlbumOpenMeResponse; // GET /v1/album/open/me + "photo.like": PhotoLikeResponse; // POST /v1/photo/{photoId}/liked + "photo.unlike": PhotoUnlikeResponse; // DELETE /v1/photo/{photoId}/unliked + "photo.presignedDownload": PhotoPresignedDownloadResponse; // POST /v1/photo/download-url + "photo.presignedUpload": PhotoPresignedUploadResponse; // POST /v1/photo/presigned-url + "photo.reportUploadResult": PhotoReportUploadResultResponse; // POST /v1/photo/report + "cheese4cut.finalize": Cheese4cutFinalizeResponse; // POST /v1/cheese4cut/{code}/fixed + "cheese4cut.preview": Cheese4cutPreviewResponse; // GET /v1/cheese4cut/{code}/preview + "internal.thumbnailComplete": InternalThumbnailCompleteResponse; // POST /internal/thumbnail/complete +} diff --git a/src/global/api/getPresignedUrl.ts b/src/global/api/getPresignedUrl.ts new file mode 100644 index 00000000..64bdc698 --- /dev/null +++ b/src/global/api/getPresignedUrl.ts @@ -0,0 +1,48 @@ +import { api } from '@/global/utils/api'; +import Toast from '../components/toast/Toast'; + +export type PresignedUrlRequest = { + albumCode: string; + fileInfos: { + fileName: string; + fileSize: number; + contentType: string; + captureTime: string; // yyyy-MM-ddTHH:mm:ss + }[]; +}; + +export type PresignedUrlInfo = { + photoId: number; + uploadUrl: string; +}; + +type ApiResponse = { + isSuccess: boolean; + code: number; + message: string; + presignedUrlInfos: PresignedUrlInfo[]; +}; + +export async function getPresignedUrl( + params: PresignedUrlRequest, +): Promise { + try { + const response = await api.post({ + path: '/v1/photo/presigned-url', + body: { albumCode: params.albumCode, fileInfos: params.fileInfos }, + }); + return response.result.presignedUrlInfos; + } catch (error: unknown) { + console.error('Presigned URL 조회 실패:', error); + + if (error && typeof error === 'object' && 'message' in error) { + Toast.alert(error.message as string); + } else if (error instanceof Error) { + Toast.alert(error.message); + } else { + Toast.alert('오류가 발생했습니다.'); + } + + throw error; + } +} diff --git a/src/global/api/presignedAndUploadToNCP.ts b/src/global/api/presignedAndUploadToNCP.ts new file mode 100644 index 00000000..a5d7edac --- /dev/null +++ b/src/global/api/presignedAndUploadToNCP.ts @@ -0,0 +1,29 @@ +import { + getPresignedUrl, + PresignedUrlRequest, +} from '@/global/api/getPresignedUrl'; +import { uploadFilesToNCP } from '@/global/api/uploadToNCP'; +import { reportFailedPhotoIds } from '@/global/hooks/useReportFailed'; + +/** + * Presigned URL을 발급받고, 해당 URL로 파일을 NCP에 업로드까지 처리하는 통합 API + * @param params PresignedUrlRequest & { files: File[] } + * @returns 업로드 결과 (성공 개수, 실패 개수, 실패 photoId) + */ +export async function presignedAndUploadToNCP( + params: PresignedUrlRequest & { files: File[] }, +): Promise<{ success: number; failed: number; failedPhotoIds: number[] }> { + const presignedUrlInfos = await getPresignedUrl(params); + + const uploadResult = await uploadFilesToNCP(params.files, presignedUrlInfos); + + if (uploadResult.failedPhotoIds.length > 0) { + try { + await reportFailedPhotoIds(uploadResult.failedPhotoIds); + } catch (e) { + console.error('실패 보고 API 호출 실패:', e); + } + } + + return uploadResult; +} diff --git a/src/global/api/uploadToNCP.ts b/src/global/api/uploadToNCP.ts new file mode 100644 index 00000000..a1e5e098 --- /dev/null +++ b/src/global/api/uploadToNCP.ts @@ -0,0 +1,72 @@ +import { PresignedUrlInfo } from '@/global/api/getPresignedUrl'; + +/** + * Presigned URL을 사용하여 NCP에 파일 업로드 + * @param file 업로드할 파일 + * @param uploadUrl NCP presigned URL + * @param photoId 사진 ID + * @returns 업로드 성공 여부 + */ +export async function uploadFileToNCP( + file: File, + uploadUrl: string, + photoId: number, +): Promise { + try { + const response = await fetch(uploadUrl, { + method: 'PUT', + body: file, + }); + + if (!response.ok) { + console.error( + `파일 업로드 실패 (photoId: ${photoId}):`, + response.statusText, + ); + return false; + } + + return true; + } catch (error) { + console.error(`파일 업로드 중 오류 발생 (photoId: ${photoId}):`, error); + return false; + } +} + +/** + * 여러 파일을 NCP에 병렬 업로드 + * @param files 업로드할 파일 배열 + * @param presignedUrlInfos presigned URL 정보 배열 + * @returns 업로드 결과 (성공 개수, 실패 개수) + */ +export async function uploadFilesToNCP( + files: File[], + presignedUrlInfos: PresignedUrlInfo[], +): Promise<{ success: number; failed: number; failedPhotoIds: number[] }> { + if (files.length !== presignedUrlInfos.length) { + console.error('파일 개수와 presigned URL 개수가 일치하지 않습니다.'); + return { + success: 0, + failed: files.length, + failedPhotoIds: presignedUrlInfos.map((i) => i.photoId), + }; + } + + const uploadResults = await Promise.all( + files.map((file, index) => { + const { uploadUrl, photoId } = presignedUrlInfos[index]; + return uploadFileToNCP(file, uploadUrl, photoId).then((ok) => ({ + ok, + photoId, + })); + }), + ); + + const success = uploadResults.filter((r) => r.ok).length; + const failed = uploadResults.length - success; + const failedPhotoIds = uploadResults + .filter((r) => !r.ok) + .map((r) => r.photoId); + + return { success, failed, failedPhotoIds }; +} diff --git a/src/global/components/CountdownTimer.tsx b/src/global/components/CountdownTimer.tsx new file mode 100644 index 00000000..cf71214e --- /dev/null +++ b/src/global/components/CountdownTimer.tsx @@ -0,0 +1,165 @@ +'use client'; +import { AnimatePresence, LazyMotion, domAnimation, m } from 'framer-motion'; +import { useEffect, useState } from 'react'; + +interface TimeLeft { + days: number; + hours: number; + minutes: number; + seconds: number; +} + +interface AnimatedNumberProps { + number: number; + label: string; +} + +interface CountdownTimerProps { + albumId: string; +} + +function calculateTimeLeft(targetDate: Date): TimeLeft { + const difference = +new Date(targetDate) - +new Date(); + let timeLeft: TimeLeft = { + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + }; + + if (difference > 0) { + timeLeft = { + days: Math.floor(difference / (1000 * 60 * 60 * 24)), + hours: Math.floor((difference / (1000 * 60 * 60)) % 24), + minutes: Math.floor((difference / 1000 / 60) % 60), + seconds: Math.floor((difference / 1000) % 60), + }; + } + + return timeLeft; +} + +function AnimatedNumber({ number, label }: AnimatedNumberProps) { + const formattedNumber = String(number).padStart(2, '0'); + + return ( +
+
+
+ + + {formattedNumber} + + +
+
+ + {label} + +
+ ); +} + +export function CountdownTimer({ albumId }: CountdownTimerProps) { + const [targetDate, setTargetDate] = useState(null); + const [timeLeft, setTimeLeft] = useState({ + days: 0, + hours: 0, + minutes: 0, + seconds: 0, + }); + + useEffect(() => { + const storageKey = `album_${albumId}_created_date`; + + const storedDate = localStorage.getItem(storageKey); + + let calculatedDate: Date; + + if (storedDate) { + calculatedDate = new Date(storedDate); + } else { + calculatedDate = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); + localStorage.setItem(storageKey, calculatedDate.toISOString()); + } + + setTargetDate(calculatedDate); + setTimeLeft(calculateTimeLeft(calculatedDate)); + }, [albumId]); + + useEffect(() => { + if (!targetDate) return; + + const timer = setInterval(() => { + const newTimeLeft = calculateTimeLeft(targetDate); + setTimeLeft(newTimeLeft); + + if ( + newTimeLeft.days === 0 && + newTimeLeft.hours === 0 && + newTimeLeft.minutes === 0 && + newTimeLeft.seconds === 0 + ) { + clearInterval(timer); + } + }, 1000); + + return () => clearInterval(timer); + }, [targetDate]); + + // 레이아웃 시프트 방지를 위해 초기 렌더링 시에도 공간 확보 + if (!targetDate) { + return ( +
+
+
+ + Days + +
+ : +
+
+ + HOURS + +
+ : +
+
+ + Mins + +
+ : +
+
+ + Secs + +
+
+ ); + } + + return ( + +
+ + : + + : + + : + +
+
+ ); +} diff --git a/src/global/components/DateXInput.tsx b/src/global/components/DateXInput.tsx new file mode 100644 index 00000000..8cd0982c --- /dev/null +++ b/src/global/components/DateXInput.tsx @@ -0,0 +1,104 @@ +'use client'; +import { Calendar } from '@/components/ui/calendar'; +import { + Popover, + PopoverContent, + PopoverTrigger, +} from '@/components/ui/popover'; +import { cn } from '@/lib/utils'; +import { format } from 'date-fns'; +import { CalendarIcon } from 'lucide-react'; +import * as React from 'react'; + +interface DateXInputProps { + label?: string; + value: string; + onChange: (value: string) => void; + error?: string; + helperText?: string; + min?: string; + max?: string; + placeholder?: string; + disabled?: boolean; + className?: string; +} + +export default function DateXInput({ + label, + value, + onChange, + error, + helperText, + min, + max, + placeholder = 'YYYY-MM-DD', + disabled, + className, +}: DateXInputProps) { + const [open, setOpen] = React.useState(false); + const parsedDate = + value && value !== '' && value !== placeholder + ? new Date(value) + : undefined; + + const minDate = min ? new Date(min) : undefined; + const maxDate = max ? new Date(max) : undefined; + + return ( +
+
+ {label && ( +
+ {label} +
+ )} + + + + + + { + if (date) { + onChange(format(date, 'yyyy-MM-dd')); + setOpen(false); + } + }} + fromDate={minDate} + toDate={maxDate} + /> + + + {(error || helperText) && ( +
+ {error || helperText} +
+ )} +
+
+ ); +} diff --git a/src/global/components/LongButton.tsx b/src/global/components/LongButton.tsx new file mode 100644 index 00000000..ebe8cc41 --- /dev/null +++ b/src/global/components/LongButton.tsx @@ -0,0 +1,53 @@ +interface LongButtonProps { + text: string; + disabled?: boolean; + onClick?: () => void; + sideGap?: number; // 좌우 여백(px) + bottomGap?: number; // 하단 여백(px) + noFixed?: boolean; // true면 fixed 해제하고 가로 100% 채움 + height?: number; // 버튼 세로(px) + safeArea?: boolean; // false면 safe-area-inset-bottom 미적용 +} + +export default function LongButton({ + text, + disabled = false, + onClick, + sideGap = 16, + bottomGap = 20, + noFixed = false, + height, + safeArea = true, +}: LongButtonProps) { + // noFixed가 true면 스타일 속성 없음, false면 left/right/bottom 값 적용 + const buttonStyle = { + ...(noFixed + ? {} + : { + left: `${sideGap}px`, + right: `${sideGap}px`, + bottom: safeArea + ? `calc(${bottomGap}px + env(safe-area-inset-bottom))` + : `${bottomGap}px`, + }), + ...(height ? { height: `${height}px` } : {}), + }; + + return ( + + ); +} diff --git a/src/global/components/Spinner.tsx b/src/global/components/Spinner.tsx new file mode 100644 index 00000000..1c6cd964 --- /dev/null +++ b/src/global/components/Spinner.tsx @@ -0,0 +1,18 @@ +export default function Spinner() { + return ( +
+ + + +
+ ); +} diff --git a/src/global/components/XInput.tsx b/src/global/components/XInput.tsx new file mode 100644 index 00000000..0608988b --- /dev/null +++ b/src/global/components/XInput.tsx @@ -0,0 +1,129 @@ +'use client'; +import { X } from 'lucide-react'; +import React, { InputHTMLAttributes, useRef, useState } from 'react'; + +interface InputProps + extends Omit, 'onChange'> { + /** 인풋 상단 라벨 텍스트 */ + label?: string; + /** 인풋 값 (항상 string) */ + value: string; + /** 값 변경 시 호출되는 콜백 (string) */ + onChange: (value: string) => void; + /** 에러 메시지 (있으면 하단에 표시) */ + error?: string; + /** 하단 서브 텍스트(설명, 안내 등) */ + helperText?: string; + /** X 버튼(입력 내용 지우기) 노출 여부, false면 아예 안보임 (기본값 true) */ + showClear?: boolean; + /** true면 입력값 없어도 X 버튼 항상 노출, false면 입력값 있을 때만 노출 (기본값 false) */ + showClearAlways?: boolean; +} + +export default function XInput({ + label, + value, + onChange, + error, + helperText, + showClear = true, + showClearAlways = false, + className, + disabled, + maxLength, + type, + ...restProps +}: InputProps) { + const [isFocused, setIsFocused] = useState(false); + const inputRef = useRef(null); + + const handleInputChange = (e: React.ChangeEvent) => { + onChange(e.target.value); + }; + + const handleClear = () => { + onChange(''); + inputRef.current?.focus(); + }; + + const handleInputClick = () => { + if (type === 'date' && inputRef.current) { + inputRef.current.showPicker?.(); + } + }; + + const shouldShowClear = + showClear && isFocused && !disabled && (showClearAlways || value); + + return ( +
+
+ {label && ( +
+ {label} +
+ )} + +
+ setIsFocused(true)} + onBlur={() => setIsFocused(false)} + disabled={disabled} + maxLength={maxLength} + className={`bg-element-gray-lighter typo-body-lg-medium text-text-basic placeholder:text-text-subtler w-full rounded-[8px] p-4 disabled:cursor-not-allowed disabled:opacity-50 ${ + error + ? 'outline-text-error outline-1' + : 'focus:outline-border-primary focus:outline-1' + } ${shouldShowClear ? 'pr-12' : ''} ${ + type === 'date' ? 'cursor-pointer' : '' + } ${ + type === 'number' + ? '[appearance:textfield] [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none' + : '' + }`} + style={ + type === 'date' && !value + ? { + color: '#8E9398', + } + : type === 'date' + ? { + color: '#18191B', + } + : undefined + } + {...restProps} + /> + + {shouldShowClear && ( + + )} +
+ + {(error || helperText) && ( +
+ {error || helperText} +
+ )} +
+
+ ); +} diff --git a/src/global/components/carousel/MarqueeCarousel.tsx b/src/global/components/carousel/MarqueeCarousel.tsx new file mode 100644 index 00000000..10e5fab0 --- /dev/null +++ b/src/global/components/carousel/MarqueeCarousel.tsx @@ -0,0 +1,40 @@ +'use client'; + +type MarqueeCarouselProps = { + items: React.ReactNode[]; + itemWidth: number; + gap?: number; + speed?: number; // px/s + className?: string; +}; + +export default function MarqueeCarousel({ + items, + itemWidth, + gap = 16, + speed = 80, + className = '', +}: MarqueeCarouselProps) { + const trackWidth = (itemWidth + gap) * items.length; + + const duration = trackWidth / speed; + + return ( +
+
+ {[...items, ...items].map((item, i) => ( +
+ {item} +
+ ))} +
+
+ ); +} diff --git a/src/global/components/header/CloseButton.tsx b/src/global/components/header/CloseButton.tsx new file mode 100644 index 00000000..f17daa17 --- /dev/null +++ b/src/global/components/header/CloseButton.tsx @@ -0,0 +1,19 @@ +'use client'; +import { X } from 'lucide-react'; +import { useRouter } from 'next/navigation'; + +interface CloseButtonProps {} + +export default function CloseButton({}: CloseButtonProps) { + const router = useRouter(); + + const handleClick = () => { + router.back(); + }; + + return ( + + ); +} diff --git a/src/global/components/header/CustomHeader.tsx b/src/global/components/header/CustomHeader.tsx new file mode 100644 index 00000000..96743756 --- /dev/null +++ b/src/global/components/header/CustomHeader.tsx @@ -0,0 +1,130 @@ +/** + * CustomHeader Component + * + * 재사용 가능한 커스텀 헤더 컴포넌트 + * - 왼쪽: 뒤로가기 버튼 + 타이틀 + * - 오른쪽: 커스텀 컴포넌트 (단일 또는 배열) + * + * @example + * // 기본 사용 (타이틀만) + * + * + * @example + * // 오른쪽에 버튼 1개 + * 저장 + * } + * /> + * + * @example + * // 오른쪽에 여러 버튼 (배열) + * 취소, + * , + * ]} + * /> + * + * @example + * // Fragment 방식 + * + * + * + * + * } + * /> + * + * @example + * // 커스텀 뒤로가기 핸들러 + * router.push('/home')} + * rightContent={} + * /> + */ + +'use client'; +import { ChevronLeft } from 'lucide-react'; +import { useRouter } from 'next/navigation'; +import { ReactNode } from 'react'; + +export const HEADER_HEIGHT = 72; + +interface CustomHeaderProps { + /** 헤더 중앙에 표시될 타이틀 */ + title: string; + /** 헤더 오른쪽에 표시될 컴포넌트들 (16px gap으로 배치) - 배열 또는 단일 컴포넌트 */ + rightContent?: ReactNode | ReactNode[]; + /** 뒤로가기 버튼 클릭 핸들러 (기본: router.back()) */ + onBackClick?: () => void; + isShowBack?: boolean; + border?: boolean; + /** 헤더를 완전히 숨길지 여부 */ + isHidden?: boolean; +} + +export default function CustomHeader({ + title, + rightContent, + onBackClick, + isShowBack, + border = true, + isHidden = false, +}: CustomHeaderProps) { + const router = useRouter(); + + function handleBackClick() { + if (onBackClick) { + onBackClick(); + } else { + router.back(); + } + } + + // 숨김 처리 + if (isHidden) return null; + + return ( + <> +
+ {/* 왼쪽: 뒤로가기 + 타이틀 */} +
+ {isShowBack && ( + + )} + + {title} +
+ + {/* 오른쪽: 커스텀 컴포넌트 */} + {rightContent && ( +
{rightContent}
+ )} +
+ {/* 헤더로 인해 가려지는 영역이 없도록 아래 요소 추가 */} +
+ + ); +} diff --git a/src/global/components/header/LogoHeader.tsx b/src/global/components/header/LogoHeader.tsx new file mode 100644 index 00000000..b2b8601d --- /dev/null +++ b/src/global/components/header/LogoHeader.tsx @@ -0,0 +1,51 @@ +'use client'; +import { useCheckAuth } from '@/global/hooks/useCheckAuth'; +import Link from 'next/link'; +import { useCallback } from 'react'; +import SvgLogo from './svg/SvgLogo'; + +interface LogoHeaderProps { + showLogin?: boolean; + bgColor?: string; + border?: boolean; +} + +export default function LogoHeader({ + showLogin = true, + bgColor = 'white', + border = false, +}: LogoHeaderProps) { + const { isAuthed } = useCheckAuth(); + + const handleLoginClick = useCallback(() => { + if (typeof document !== 'undefined') { + document.cookie = 'entry=main; path=/;'; + } + }, []); + const shouldShowLogin = showLogin && !isAuthed; + + return ( + <> +
+
+ + {shouldShowLogin && ( + +
+ + 로그인 + +
+ + )} +
+
+ + {/* 헤더로인해 가려지는 영역 방지 */} +
+ + ); +} diff --git a/src/global/components/header/svg/SvgLogo.tsx b/src/global/components/header/svg/SvgLogo.tsx new file mode 100644 index 00000000..c23a990f --- /dev/null +++ b/src/global/components/header/svg/SvgLogo.tsx @@ -0,0 +1,38 @@ +interface SvgLogoProps {} + +export default function SvgLogo({}: SvgLogoProps) { + return ( + + + + + + + ); +} diff --git a/src/global/components/modal/BottomSheetModal.tsx b/src/global/components/modal/BottomSheetModal.tsx new file mode 100644 index 00000000..63d659f1 --- /dev/null +++ b/src/global/components/modal/BottomSheetModal.tsx @@ -0,0 +1,84 @@ +'use client'; + +import { + Drawer, + DrawerClose, + DrawerContent, + DrawerTitle, + DrawerTrigger, +} from '@/components/ui/drawer'; +import { cn } from '@/lib/utils'; +import React from 'react'; + +type BottomSheetModalProps = { + /** 트리거 버튼/노드 (예: ) */ + trigger: React.ReactNode; + /** 모달 className (높이, 너비, 스타일 등) */ + className?: string; + /** 모달 내용 */ + children: React.ReactNode; + /** 닫기 버튼 표시 여부 */ + showCloseButton?: boolean; + /** 모달 제목 (화면에 표시, 없으면 스크린리더용만 사용) */ + title?: string; + /** 드래그/스와이프 등으로 닫기 허용 여부 (기본값: true) */ + dismissible?: boolean; + /** 상단 드래그 바 표시 여부 (기본값: true) */ + showHandle?: boolean; + /** 외부에서 열림 상태를 제어하고 싶을 때 사용 */ + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (open: boolean) => void; +}; + +export default function BottomSheetModal({ + trigger, + children, + className = 'max-h-[80vh] w-full', + showCloseButton = false, + title, + dismissible = true, + showHandle = true, + open, + defaultOpen, + onOpenChange, +}: BottomSheetModalProps) { + return ( + + {trigger} + + +
+ {/* 스크린리더용 제목 (항상 필요) */} + + {title || '모달'} + + + {showCloseButton && ( +
+ + ✕ + +
+ )} + {children} +
+
+
+ ); +} diff --git a/src/global/components/modal/ConfirmModal.tsx b/src/global/components/modal/ConfirmModal.tsx new file mode 100644 index 00000000..a4d255dd --- /dev/null +++ b/src/global/components/modal/ConfirmModal.tsx @@ -0,0 +1,128 @@ +'use client'; + +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { cn } from '@/lib/utils'; +import { useCallback, useEffect } from 'react'; + +type ConfirmModalProps = { + /** 트리거 버튼/노드 (예: ) */ + trigger: React.ReactNode; + /** 모달 제목 */ + title: string | React.ReactNode; + /** 모달 본문 */ + description?: string | React.ReactNode; + /** 취소 버튼 라벨 */ + cancelText?: string; + /** 확인 버튼 라벨 */ + confirmText?: string; + /** 취소 클릭 핸들러 */ + onCancel?: () => void; + /** 확인 클릭 핸들러 (Promise 허용) */ + onConfirm?: () => Promise | void; + /** 버튼 스타일을 커스터마이즈하고 싶다면 className 전달(선택) */ + cancelClassName?: string; + confirmClassName?: string; +}; + +export default function ConfirmModal({ + trigger, + title, + description, + cancelText = '취소', + confirmText = '확인', + onCancel, + onConfirm, + cancelClassName = '', + confirmClassName = '', +}: ConfirmModalProps) { + const handleConfirm = useCallback(async () => { + try { + await onConfirm?.(); + } catch (e) { + console.error(e); + } + }, [onConfirm]); + + const handleCancel = useCallback(() => { + onCancel?.(); + }, [onCancel]); + + useEffect(() => { + // data-scroll-locked 속성 제거 + const removeScrollLock = () => { + const body = document.body; + if (body.hasAttribute('data-scroll-locked')) { + body.removeAttribute('data-scroll-locked'); + } + }; + + removeScrollLock(); + const interval = setInterval(removeScrollLock, 100); + + return () => { + clearInterval(interval); + removeScrollLock(); + }; + }, []); + + return ( + + {trigger} + + + + + {title} + + {description ? ( + + {description} + + ) : null} + + + + + {cancelText} + + + + {confirmText} + + + + + ); +} diff --git a/src/global/components/photo/PhotoBox.tsx b/src/global/components/photo/PhotoBox.tsx new file mode 100644 index 00000000..46d9b67c --- /dev/null +++ b/src/global/components/photo/PhotoBox.tsx @@ -0,0 +1,142 @@ +'use client'; +import { cn } from '@/lib/utils'; +import { Check, Heart } from 'lucide-react'; +import { useEffect, useState } from 'react'; + +const FALLBACK_SRC = '/icon/error-image.svg'; + +interface PhotoBoxProps { + size?: number; // px + /** 부모 컨테이너 크기를 따라가기 여부 */ + responsive?: boolean; + likeCount?: number; + liked?: boolean; + mode?: 'default' | 'select'; + downloaded?: boolean; + pressed?: boolean; + disabled?: boolean; + imageSrc?: string; + imageAlt?: string; + onPress?: (pressed: boolean) => void; + onDisabledPress?: () => void; + pressable?: boolean; +} + +export default function PhotoBox({ + size = 82, + responsive = false, + likeCount, + liked = false, + downloaded = false, + pressed = false, + mode = 'default', + disabled = false, + imageSrc, + imageAlt = '사진', + onPress, + onDisabledPress, + pressable = true, +}: PhotoBoxProps) { + const showLike = likeCount !== undefined; + const [currentSrc, setCurrentSrc] = useState(imageSrc ?? FALLBACK_SRC); + + useEffect(() => { + setCurrentSrc(imageSrc ?? FALLBACK_SRC); + }, [imageSrc]); + + const handleImageError = (): void => { + if (currentSrc === FALLBACK_SRC) return; + setCurrentSrc(FALLBACK_SRC); + }; + + const handlePress = () => { + if (disabled) { + onDisabledPress?.(); + return; + } + + if (!pressable) return; + + const next = !pressed; + onPress?.(next); + }; + + return ( + + ); +} diff --git a/src/global/components/portal/BodyPortal.tsx b/src/global/components/portal/BodyPortal.tsx new file mode 100644 index 00000000..4deb590a --- /dev/null +++ b/src/global/components/portal/BodyPortal.tsx @@ -0,0 +1,19 @@ +'use client'; + +import type { ReactNode, ReactPortal } from 'react'; +import { createPortal } from 'react-dom'; + +interface BodyPortalProps { + children: ReactNode; + container?: Element; +} + +/** Render children into a DOM container (defaults to document.body) */ +export function BodyPortal({ + children, + container, +}: BodyPortalProps): ReactPortal | null { + if (typeof document === 'undefined') return null; + + return createPortal(children, container ?? document.body); +} diff --git a/src/global/components/toast/AlbumToast.tsx b/src/global/components/toast/AlbumToast.tsx new file mode 100644 index 00000000..cf91ca15 --- /dev/null +++ b/src/global/components/toast/AlbumToast.tsx @@ -0,0 +1,100 @@ +import { AlertCircle } from 'lucide-react'; +import { useEffect, useState } from 'react'; + +type AlbumToastProps = { + message: string; + style?: React.CSSProperties; + onDismiss?: () => void; + delay?: number; // 사라지기 시작하는 시간에 추가할 delay +}; + +export default function AlbumToast({ + message, + style, + onDismiss, + delay = 0, +}: AlbumToastProps) { + const bottomPx = style?.bottom ?? 88; + const [visible, setVisible] = useState(true); + const [shouldRender, setShouldRender] = useState(true); + + useEffect(() => { + const timer = setTimeout(() => { + setVisible(false); + // fade-out 트랜지션 후 실제로 unmount + setTimeout(() => { + setShouldRender(false); + onDismiss?.(); + }, 400); // 트랜지션 시간과 맞춤 + }, 4000 + delay); + return () => clearTimeout(timer); + }, [onDismiss, delay]); + + if (!shouldRender) return null; + + return ( +
+
+ + {message} +
+
+ ); +} + +export function AlbumToastList({ + toasts, + onRemove, +}: { + toasts: string[]; + onRemove?: (message: string) => void; +}) { + const TOAST_HEIGHT = 48; // px + const GAP = 12; // px + const [visibleToasts, setVisibleToasts] = useState< + Array<{ id: number; message: string }> + >([]); + + useEffect(() => { + // 새로운 토스트가 추가되면 기존 것에 추가 (중복 방지) + setVisibleToasts((prev) => { + const newToasts = toasts + .filter((msg) => !prev.some((t) => t.message === msg)) + .map((message) => ({ + id: Date.now() + Math.random(), + message, + })); + return newToasts.length > 0 ? [...prev, ...newToasts] : prev; + }); + }, [toasts]); + + const handleDismiss = (id: number, message: string) => { + setVisibleToasts((prev) => prev.filter((toast) => toast.id !== id)); + onRemove?.(message); + }; + + return ( + <> + {visibleToasts.map((toast, idx) => ( + handleDismiss(toast.id, toast.message)} + /> + ))} + + ); +} diff --git a/src/global/components/toast/Toast.tsx b/src/global/components/toast/Toast.tsx new file mode 100644 index 00000000..3698598b --- /dev/null +++ b/src/global/components/toast/Toast.tsx @@ -0,0 +1,74 @@ +import { CircleCheck } from 'lucide-react'; +import React from 'react'; +import { createRoot, Root } from 'react-dom/client'; + +const TOAST_CONTAINER_ID = '__toast-container'; + +// HTMLElement에 커스텀 프로퍼티 하나 더 붙여서 root 저장 +interface ToastContainerElement extends HTMLElement { + __root?: Root; +} + +const getContainer = (id: string): ToastContainerElement => { + let toastContainer = document.getElementById( + id, + ) as ToastContainerElement | null; + + if (!toastContainer) { + toastContainer = document.createElement('div') as ToastContainerElement; + toastContainer.id = id; + toastContainer.style.display = 'flex'; + toastContainer.style.justifyContent = 'center'; + document.body.appendChild(toastContainer); + } + + return toastContainer; +}; + +const renderToast = ( + component: React.ReactNode, + container: ToastContainerElement, +): void => { + // 이미 root가 있으면 그거 쓰고, 없으면 새로 만든다 + if (!container.__root) { + container.__root = createRoot(container); + } + + container.__root.render(component); +}; + +const unmountToast = ( + toastContainer: ToastContainerElement, + ms = 3000, +): void => { + setTimeout(() => { + // 만들어둔 root가 있으면 그걸로 언마운트 + toastContainer.__root?.unmount(); + toastContainer.remove(); + }, ms); +}; + +const Toast = { + alert: async (message: string): Promise => { + const toastContainer = getContainer(TOAST_CONTAINER_ID); + const { default: ToastView } = await import('./ToastView'); + + renderToast(, toastContainer); + unmountToast(toastContainer); + }, + check: async (message: string): Promise => { + const toastContainer = getContainer(TOAST_CONTAINER_ID); + const { default: ToastView } = await import('./ToastView'); + + renderToast( + } + />, + toastContainer, + ); + unmountToast(toastContainer); + }, +}; + +export default Toast; diff --git a/src/global/components/toast/ToastView.tsx b/src/global/components/toast/ToastView.tsx new file mode 100644 index 00000000..3f109960 --- /dev/null +++ b/src/global/components/toast/ToastView.tsx @@ -0,0 +1,18 @@ +import { AlertCircle } from 'lucide-react'; + +interface ToastViewProps { + lucideIcon?: React.ReactNode; + message: string; +} + +export default function ToastView({ lucideIcon, message }: ToastViewProps) { + return ( +
+
+ {lucideIcon ?? } + + {message} +
+
+ ); +} diff --git a/src/global/components/tooltip/BubbleTooltip.tsx b/src/global/components/tooltip/BubbleTooltip.tsx new file mode 100644 index 00000000..c152c7a7 --- /dev/null +++ b/src/global/components/tooltip/BubbleTooltip.tsx @@ -0,0 +1,35 @@ +import { cn } from '@/lib/utils'; + +interface BubbleTooltipProps { + /** 텍스트나 아이콘이 함께 포함된 문장 */ + message: string; + /** 기본은 가운데 정렬 */ + align?: 'center' | 'left' | 'right'; + className?: string; +} + +export default function BubbleTooltip({ + message, + align = 'center', + className = '', +}: BubbleTooltipProps) { + const alignClass = + align === 'center' ? 'mx-auto' : align === 'left' ? 'mr-auto' : 'ml-auto'; + + return ( +
+
+
+ {message} +
+ + {/* 꼬다리 */} +
+
+
+
+
+ ); +} diff --git a/src/global/constants/cookies.ts b/src/global/constants/cookies.ts new file mode 100644 index 00000000..d5327f47 --- /dev/null +++ b/src/global/constants/cookies.ts @@ -0,0 +1,2 @@ +export const ACCESS_TOKEN_KEY = 'ACCESS_TOKEN'; +export const REFRESH_TOKEN_KEY = 'REFRESH_TOKEN'; diff --git a/src/global/constants/images.ts b/src/global/constants/images.ts new file mode 100644 index 00000000..83f650cc --- /dev/null +++ b/src/global/constants/images.ts @@ -0,0 +1,2 @@ +export const DEFAULT_PROFILE_IMAGE = + 'https://say-cheese-profile.edge.naverncp.com/profile/sign_up_profile_1.jpg'; diff --git a/src/global/context/KakaoProvider.tsx b/src/global/context/KakaoProvider.tsx new file mode 100644 index 00000000..73c1695a --- /dev/null +++ b/src/global/context/KakaoProvider.tsx @@ -0,0 +1,29 @@ +'use client'; + +import Script from 'next/script'; + +interface KakaoProviderProps { + children: React.ReactNode; +} + +export default function KakaoProvider({ children }: KakaoProviderProps) { + const kakaoKey = process.env.NEXT_PUBLIC_KAKAO_JS_KEY || ''; + + return ( + <> +