diff --git a/.coderabbit.yaml b/.coderabbit.yaml
new file mode 100644
index 00000000..0ad80259
--- /dev/null
+++ b/.coderabbit.yaml
@@ -0,0 +1,14 @@
+language: 'ko-KR'
+early_access: false
+reviews:
+ profile: 'chill'
+ request_changes_workflow: false
+ high_level_summary: true
+ poem: false
+ review_status: true
+ collapse_walkthrough: false
+ auto_review:
+ enabled: true
+ drafts: false
+chat:
+ auto_reply: true
diff --git a/.github/ISSUE_TEMPLATE/-feature--.md b/.github/ISSUE_TEMPLATE/-feature--.md
new file mode 100644
index 00000000..a91a3be8
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/-feature--.md
@@ -0,0 +1,20 @@
+---
+name: "[FEATURE] "
+about: ๊ธฐ๋ฅ ์์ฑ
+title: '[FEATURE]'
+labels: ''
+assignees: ''
+
+---
+
+### ๐ Description
+
+- ๊ตฌํํ ๋ด์ฉ 1
+- ๊ตฌํํ ๋ด์ฉ 2
+
+---
+
+### ๐ Todo
+
+- [ ] ๊ตฌํํ ๋ด์ฉ 1
+- [ ] ๊ตฌํํ ๋ด์ฉ 2
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 00000000..75e3fb6e
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,35 @@
+## โ
PR ์ ํ
+์ด๋ค ๋ณ๊ฒฝ ์ฌํญ์ด ์์๋์?
+
+- [ ] ์๋ก์ด ๊ธฐ๋ฅ ์ถ๊ฐ
+- [ ] ๋ฒ๊ทธ ์์
+- [ ] ์ฝ๋์ ์ํฅ์ ์ฃผ์ง ์๋ ๋ณ๊ฒฝ์ฌํญ(์คํ ์์ , ํญ ์ฌ์ด์ฆ ๋ณ๊ฒฝ, ๋ณ์๋ช
๋ณ๊ฒฝ)
+- [ ] ์ฝ๋ ๋ฆฌํฉํ ๋ง
+- [ ] ์ฃผ์ ์ถ๊ฐ ๋ฐ ์์
+- [ ] ๋ฌธ์ ์์
+- [ ] ๋น๋ ๋ถ๋ถ ํน์ ํจํค์ง ๋งค๋์ ์์
+- [ ] ํ์ผ ํน์ ํด๋๋ช
์์
+- [ ] ํ์ผ ํน์ ํด๋ ์ญ์
+
+---
+
+### ๐ ๊ด๋ จ ์ด์๋ฒํธ
+
+- Closed #1
+
+---
+
+### โ
Key Changes
+
+-
+-
+
+---
+
+### ๐ธ ์คํฌ๋ฆฐ์ท or ์คํ์์
+
+
+
+---
+
+## ๐ธ ๊ธฐํ ์ฌํญ or ์ถ๊ฐ ์ฝ๋ฉํธ
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000..164c08fd
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,31 @@
+name: CI/CD Build Check
+
+on:
+ push:
+ branches: ['main', 'develop']
+ pull_request:
+ branches: ['main', 'develop']
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+
+ - name: Install Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 22
+
+ - name: Install pnpm
+ uses: pnpm/action-setup@v2
+ with:
+ version: 10
+
+ - name: Install dependencies
+ run: pnpm install
+
+ - name: Build check
+ run: pnpm run build
diff --git a/.github/workflows/production.yml b/.github/workflows/production.yml
new file mode 100644
index 00000000..d6945dab
--- /dev/null
+++ b/.github/workflows/production.yml
@@ -0,0 +1,31 @@
+name: Production Deployment
+
+on:
+ push:
+ branches:
+ - develop
+
+jobs:
+ Deploy-Preview:
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v2
+ - run: curl -X POST -d {} "https://webhooks.amplify.us-east-1.amazonaws.com/prod/webhooks?id=ea04170f-ab4c-443a-b01f-e2e53c32b641&token=KSqj5xnbmdNs92jcEzHWSP84O6dVe0r1Rq6FCuL5E&operation=startbuild" -H "Content-Type:application/json"
+
+ - name: Checkout Repository
+ uses: actions/checkout@v2
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install pnpm
+ run: npm install -g pnpm
+
+ - name: Install Dependencies
+ run: pnpm install
+
+ - name: Build Library
+ run: pnpm run build
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..5ef6a520
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 00000000..2d9dda3d
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,39 @@
+# Prettier ignore file
+
+# Dependencies
+node_modules/
+.pnp
+.pnp.js
+
+# Build outputs
+.next/
+out/
+dist/
+build/
+
+# Environment files
+.env*
+
+# Lock files
+package-lock.json
+yarn.lock
+pnpm-lock.yaml
+
+# Coverage
+coverage/
+
+# Logs
+*.log
+logs/
+
+# OS generated files
+.DS_Store
+Thumbs.db
+
+# IDE files
+.vscode/
+.idea/
+
+# Temporary files
+*.tmp
+*.temp
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..ecaf6dba
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,11 @@
+{
+ "semi": true,
+ "singleQuote": true,
+ "trailingComma": "all",
+ "tabWidth": 2,
+ "useTabs": false,
+ "printWidth": 100,
+ "bracketSpacing": true,
+ "arrowParens": "always",
+ "plugins": ["prettier-plugin-tailwindcss"]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..0141b154
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,27 @@
+{
+ "editor.formatOnSave": true,
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.codeActionsOnSave": {
+ "source.fixAll.eslint": "explicit"
+ },
+ "prettier.requireConfig": true,
+ "prettier.configPath": ".prettierrc",
+ "[javascript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[javascriptreact]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[typescript]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[typescriptreact]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[json]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ },
+ "[css]": {
+ "editor.defaultFormatter": "esbenp.prettier-vscode"
+ }
+}
\ No newline at end of file
diff --git a/README.md b/README.md
index 2a98f0d5..ad973530 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,111 @@
-# TEAMFICIAL-FE
\ No newline at end of file
+# ๐ฃ๏ธ ํจ๊ป์ ์์์ ๋ถ๋๋ฝ๊ฒ, ํ๋น๋ฉ ์๋น์ค "ํํผ์
"
+
+
+
+# ํ๋ก์ ํธ ์๊ฐ
+ํํผ์
์ ์คํ์ด ์๋ โํจ๊ป ์ผํด๋ณธ ์ฌ๋๋ค์ ์ง์ง ์ด์ผ๊ธฐโ๋ก ๋๋ฅผ ๋ณด์ฌ์ฃผ๋
+์ํํธ์คํฌ ๊ธฐ๋ฐ์ ํ๋น๋ฉ ํ๋ซํผ์
๋๋ค.
+
+์ง๋ฌธ ํ
ํ๋ฆฟ์ ํตํด ๋๋ฃ๋ค์ด ๊ธฐ๋กํ ๋์ ํ์
์ฑํฅ, ์ผํ๋ ํ๋,
+์ปค๋ฎค๋์ผ์ด์
์คํ์ผ์ ํฌ๋ช
ํ๊ฒ ์ ๊ณตํฉ๋๋ค.
+
+ํํผ์
์ ๋ ์ ๋ขฐํ ์ ์๋ ํ์
, ๋ ์กฐํ๋ก์ด ํ์ํฌ๋ก ์ด์ด์ง๋ ๊ณผ์
+์ ๋ฐ์ ์ค๊ณํฉ๋๋ค.
+
+
+
+
+
+
+
+
+
+# ์ฃผ์๊ธฐ๋ฅ
+
+
+
+
+
+
+### ํ๋ก์ ํธ ๋ทฐ & ์ง์ํ๊ธฐ
+
+
+
+
+
+# ๊ธฐ์ ์คํ
+
+| ์ญํ | ๊ธฐ์ / ๋๊ตฌ |
+|------|-------------|
+| **Frontend** |
|
+
+# FE ํ์
+
+
+
+
+# ํด๋ ๊ตฌ์กฐ
+```
+src
+โโโ app # Next.js App Router (ํ์ด์ง ๋ผ์ฐํ
)
+โ โโโ (auth) # ์์
๋ก๊ทธ์ธ & ์ธ์ฆ ํ์ด์ง
+โ โโโ (main) # ๋ก๊ทธ์ธ ํ ๋ฉ์ธ ๊ธฐ๋ฅ ์์ญ
+โ โ โโโ mypage # ๋ง์ดํ์ด์ง
+โ โ โโโ project # ํ๋ก์ ํธ ์์ธ/๋ชฉ๋ก
+โ โ โโโ team # ํ ์์ธ/ํ ๊ด๋ฆฌ
+โ โ โโโ applied # ๋ด๊ฐ ์ง์ํ ํ ๋ฆฌ์คํธ
+โ โ โโโ applicant # ์ง์์ ๊ด๋ฆฌ
+โ โ โโโ question # ํํผ์
์ง๋ฌธ ์์ฑ
+โ โ โโโ recruit # ํ ๋ชจ์ง ๋ฑ๋ก
+โ โ โโโ teamps ylog # ํํผ์
๋ก ํ์ด์ง
+โ โโโ layout.tsx
+โ
+โโโ components # ์ ์ญ ๊ณตํต UI ์ปดํฌ๋ํธ
+โ โโโ common # ๋ฒํผ, ํ ์คํธ, ๋ก๋ฉ ๋ฑ ๊ธฐ๋ณธ UI
+โ โโโ login # ๋ก๊ทธ์ธ ์ ์ฉ UI
+โ โโโ modal # ์ ์ญ ๋ชจ๋ฌ ์ปดํฌ๋ํธ
+โ
+โโโ constants # ์์, dropdown options ๋ฑ
+โ
+โโโ contexts # Modal / Toast ๋ฑ ๊ธ๋ก๋ฒ ์ปจํ
์คํธ
+โ
+โโโ hooks # ์ฌ์ฌ์ฉ ๊ฐ๋ฅํ custom hooks
+โ โโโ mutation # API POST/PUT/DELETE ํ
+โ โโโ queries # API GET ํ
+โ
+โโโ libs # API ๋ ์ด์ด, axios wrapper, request ํจ์
+โ โโโ api # API ์๋ํฌ์ธํธ ๋ชจ๋
+โ
+โโโ schemas # Zod ์คํค๋ง (form validation)
+โ
+โโโ store # Zustand ์ ์ญ ์ํ ๊ด๋ฆฌ
+โ
+โโโ styles # ๊ธ๋ก๋ฒ ์คํ์ผ, SCSS ๋ณ์
+โ
+โโโ types # TypeScript ํ์
์ ์
+โ
+โโโ utils # ๊ณตํต ์ ํธ ํจ์ ๋ชจ์
+```
+
+
+# ์์คํ
์ํคํ
์ฒ
+
+
+
+
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 00000000..60f7af38
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,19 @@
+import { dirname } from 'path';
+import { fileURLToPath } from 'url';
+import { FlatCompat } from '@eslint/eslintrc';
+
+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'),
+ {
+ ignores: ['node_modules/**', '.next/**', 'out/**', 'build/**', 'next-env.d.ts'],
+ },
+];
+
+export default eslintConfig;
diff --git a/next.config.ts b/next.config.ts
new file mode 100644
index 00000000..d2f7a4f6
--- /dev/null
+++ b/next.config.ts
@@ -0,0 +1,15 @@
+import type { NextConfig } from 'next';
+
+const nextConfig: NextConfig = {
+ images: {
+ remotePatterns: [
+ {
+ protocol: 'https',
+ hostname: 'kr.object.ncloudstorage.com',
+ pathname: '**',
+ },
+ ],
+ },
+};
+
+export default nextConfig;
diff --git a/package.json b/package.json
new file mode 100644
index 00000000..dce80daf
--- /dev/null
+++ b/package.json
@@ -0,0 +1,64 @@
+{
+ "name": "teamficial-fe",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build --turbopack",
+ "start": "next start",
+ "lint": "eslint"
+ },
+ "dependencies": {
+ "@hookform/resolvers": "^5.2.2",
+ "@tailwindcss/typography": "^0.5.19",
+ "@tanstack/react-query": "^5.90.5",
+ "@tiptap/core": "^3.7.2",
+ "@tiptap/extension-document": "^3.7.2",
+ "@tiptap/extension-heading": "^3.7.2",
+ "@tiptap/extension-link": "^3.7.2",
+ "@tiptap/extension-list": "^3.7.2",
+ "@tiptap/extension-paragraph": "^3.7.2",
+ "@tiptap/extension-placeholder": "^3.7.2",
+ "@tiptap/extension-text": "^3.7.2",
+ "@tiptap/extension-underline": "^3.7.2",
+ "@tiptap/pm": "^3.7.2",
+ "@tiptap/react": "^3.7.2",
+ "@tiptap/starter-kit": "^3.7.2",
+ "axios": "^1.12.2",
+ "clsx": "^2.1.1",
+ "date-fns": "^4.1.0",
+ "framer-motion": "^12.23.24",
+ "isomorphic-dompurify": "^2.30.1",
+ "jsdom": "^27.0.1",
+ "next": "16.0.10",
+ "qs": "^6.14.0",
+ "react": "19.1.0",
+ "react-datepicker": "^8.7.0",
+ "react-dom": "19.1.0",
+ "react-hook-form": "^7.66.0",
+ "react-intersection-observer": "^10.0.0",
+ "swiper": "^12.0.3",
+ "tailwind-merge": "^3.3.1",
+ "zod": "^4.1.12",
+ "zustand": "^5.0.8"
+ },
+ "devDependencies": {
+ "@eslint/eslintrc": "^3",
+ "@svgr/cli": "^8.1.0",
+ "@svgr/webpack": "^8.1.0",
+ "@tailwindcss/postcss": "^4",
+ "@types/axios": "^0.14.4",
+ "@types/node": "^20",
+ "@types/qs": "^6.14.0",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9",
+ "eslint-config-next": "15.5.4",
+ "prettier": "^3.6.2",
+ "prettier-plugin-tailwindcss": "^0.6.14",
+ "sass": "^1.93.2",
+ "tailwindcss": "^4",
+ "typescript": "^5"
+ },
+ "packageManager": "pnpm@10.25.0+sha512.5e82639027af37cf832061bcc6d639c219634488e0f2baebe785028a793de7b525ffcd3f7ff574f5e9860654e098fe852ba8ac5dd5cefe1767d23a020a92f501"
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 00000000..ac96705e
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,7422 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ '@hookform/resolvers':
+ specifier: ^5.2.2
+ version: 5.2.2(react-hook-form@7.66.0(react@19.1.0))
+ '@tailwindcss/typography':
+ specifier: ^0.5.19
+ version: 0.5.19(tailwindcss@4.1.13)
+ '@tanstack/react-query':
+ specifier: ^5.90.5
+ version: 5.90.5(react@19.1.0)
+ '@tiptap/core':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/extension-document':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-heading':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-link':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/extension-list':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/extension-paragraph':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-placeholder':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/extensions@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))
+ '@tiptap/extension-text':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-underline':
+ specifier: ^3.7.2
+ version: 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/pm':
+ specifier: ^3.7.2
+ version: 3.7.2
+ '@tiptap/react':
+ specifier: ^3.7.2
+ version: 3.7.2(@floating-ui/dom@1.7.4)(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)(@types/react-dom@19.1.9(@types/react@19.1.16))(@types/react@19.1.16)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@tiptap/starter-kit':
+ specifier: ^3.7.2
+ version: 3.7.2
+ axios:
+ specifier: ^1.12.2
+ version: 1.12.2
+ clsx:
+ specifier: ^2.1.1
+ version: 2.1.1
+ date-fns:
+ specifier: ^4.1.0
+ version: 4.1.0
+ framer-motion:
+ specifier: ^12.23.24
+ version: 12.23.24(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ isomorphic-dompurify:
+ specifier: ^2.30.1
+ version: 2.30.1(postcss@8.5.6)
+ jsdom:
+ specifier: ^27.0.1
+ version: 27.0.1(postcss@8.5.6)
+ next:
+ specifier: 16.0.10
+ version: 16.0.10(@babel/core@7.28.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.93.2)
+ qs:
+ specifier: ^6.14.0
+ version: 6.14.0
+ react:
+ specifier: 19.1.0
+ version: 19.1.0
+ react-datepicker:
+ specifier: ^8.7.0
+ version: 8.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ react-dom:
+ specifier: 19.1.0
+ version: 19.1.0(react@19.1.0)
+ react-hook-form:
+ specifier: ^7.66.0
+ version: 7.66.0(react@19.1.0)
+ react-intersection-observer:
+ specifier: ^10.0.0
+ version: 10.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ swiper:
+ specifier: ^12.0.3
+ version: 12.0.3
+ tailwind-merge:
+ specifier: ^3.3.1
+ version: 3.3.1
+ zod:
+ specifier: ^4.1.12
+ version: 4.1.12
+ zustand:
+ specifier: ^5.0.8
+ version: 5.0.8(@types/react@19.1.16)(react@19.1.0)(use-sync-external-store@1.6.0(react@19.1.0))
+ devDependencies:
+ '@eslint/eslintrc':
+ specifier: ^3
+ version: 3.3.1
+ '@svgr/cli':
+ specifier: ^8.1.0
+ version: 8.1.0(typescript@5.9.2)
+ '@svgr/webpack':
+ specifier: ^8.1.0
+ version: 8.1.0(typescript@5.9.2)
+ '@tailwindcss/postcss':
+ specifier: ^4
+ version: 4.1.13
+ '@types/axios':
+ specifier: ^0.14.4
+ version: 0.14.4
+ '@types/node':
+ specifier: ^20
+ version: 20.19.18
+ '@types/qs':
+ specifier: ^6.14.0
+ version: 6.14.0
+ '@types/react':
+ specifier: ^19
+ version: 19.1.16
+ '@types/react-dom':
+ specifier: ^19
+ version: 19.1.9(@types/react@19.1.16)
+ eslint:
+ specifier: ^9
+ version: 9.36.0(jiti@2.6.0)
+ eslint-config-next:
+ specifier: 15.5.4
+ version: 15.5.4(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ prettier:
+ specifier: ^3.6.2
+ version: 3.6.2
+ prettier-plugin-tailwindcss:
+ specifier: ^0.6.14
+ version: 0.6.14(prettier@3.6.2)
+ sass:
+ specifier: ^1.93.2
+ version: 1.93.2
+ tailwindcss:
+ specifier: ^4
+ version: 4.1.13
+ typescript:
+ specifier: ^5
+ version: 5.9.2
+
+packages:
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@asamuzakjp/css-color@4.0.5':
+ resolution: {integrity: sha512-lMrXidNhPGsDjytDy11Vwlb6OIGrT3CmLg3VWNFyWkLWtijKl7xjvForlh8vuj0SHGjgl4qZEQzUmYTeQA2JFQ==}
+
+ '@asamuzakjp/dom-selector@6.7.3':
+ resolution: {integrity: sha512-kiGFeY+Hxf5KbPpjRLf+ffWbkos1aGo8MBfd91oxS3O57RgU3XhZrt/6UzoVF9VMpWbC3v87SRc9jxGrc9qHtQ==}
+
+ '@asamuzakjp/nwsapi@2.3.9':
+ resolution: {integrity: sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==}
+
+ '@babel/code-frame@7.27.1':
+ resolution: {integrity: sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.28.4':
+ resolution: {integrity: sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.28.4':
+ resolution: {integrity: sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.28.3':
+ resolution: {integrity: sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ resolution: {integrity: sha512-fXSwMQqitTGeHLBC08Eq5yXz2m37E4pJX1qAU1+2cNedz/ifv/bVXft90VeSav5nFO61EcNgwr0aJxbyPaWBPg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.27.2':
+ resolution: {integrity: sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-create-class-features-plugin@7.28.3':
+ resolution: {integrity: sha512-V9f6ZFIYSLNEbuGA/92uOvYsGCJNsuA8ESZ4ldc09bWk/j8H8TKiPw8Mk1eG6olpnO0ALHJmYfZvF4MEE4gajg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-create-regexp-features-plugin@7.27.1':
+ resolution: {integrity: sha512-uVDC72XVf8UbrH5qQTc18Agb8emwjTiZrQE11Nv3CuBEZmVvTwwE9CBUEvHku06gQCAyYf8Nv6ja1IN+6LMbxQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-define-polyfill-provider@0.6.5':
+ resolution: {integrity: sha512-uJnGFcPsWQK8fvjgGP5LZUZZsYGIoPeRjSF5PGwrelYgq7Q15/Ft9NGFp1zglwgIv//W0uG4BevRuSJRyylZPg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ '@babel/helper-globals@7.28.0':
+ resolution: {integrity: sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ resolution: {integrity: sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.27.1':
+ resolution: {integrity: sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.28.3':
+ resolution: {integrity: sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ resolution: {integrity: sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-plugin-utils@7.27.1':
+ resolution: {integrity: sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-remap-async-to-generator@7.27.1':
+ resolution: {integrity: sha512-7fiA521aVw8lSPeI4ZOD3vRFkoqkJcS+z4hFo82bFSH/2tNd6eJ5qCVMS5OzDmZh/kaHQeBaeyxK6wljcPtveA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-replace-supers@7.27.1':
+ resolution: {integrity: sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ resolution: {integrity: sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-string-parser@7.27.1':
+ resolution: {integrity: sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.27.1':
+ resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.27.1':
+ resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-wrap-function@7.28.3':
+ resolution: {integrity: sha512-zdf983tNfLZFletc0RRXYrHrucBEg95NIFMkn6K9dbeMYnsgHaSBGcQqdsCSStG2PYwRre0Qc2NNSCXbG+xc6g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.28.4':
+ resolution: {integrity: sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.28.4':
+ resolution: {integrity: sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1':
+ resolution: {integrity: sha512-QPG3C9cCVRQLxAVwmefEmwdTanECuUBMQZ/ym5kiw3XKCGA7qkuQLcjWWHcrD/GKbn/WmJwaezfuuAOcyKlRPA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1':
+ resolution: {integrity: sha512-qNeq3bCKnGgLkEXUuFry6dPlGfCdQNZbn7yUAPCInwAJHMU7THJfrBSozkcWq5sNM6RcF3S8XyQL2A52KNR9IA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1':
+ resolution: {integrity: sha512-g4L7OYun04N1WyqMNjldFwlfPCLVkgB54A/YCXICZYBsvJJE3kByKv9c9+R/nAfmIfjl2rKYLNyMHboYbZaWaA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1':
+ resolution: {integrity: sha512-oO02gcONcD5O1iTLi/6frMJBIwWEHceWGSGqrpCmEL8nogiS6J9PBlE48CaK20/Jx1LuRml9aDftLgdjXT8+Cw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.13.0
+
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3':
+ resolution: {integrity: sha512-b6YTX108evsvE4YgWyQ921ZAFFQm3Bn+CA3+ZXlNVnPhx+UfsVURoPjfGAPCjBgrqo30yX/C2nZGX96DxvR9Iw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2':
+ resolution: {integrity: sha512-SOSkfJDddaM7mak6cPEpswyTRnuRltl429hMraQEglW+OkovnCzsiszTmsrlY//qLFjCpQDFRvjdm2wA5pPm9w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-assertions@7.27.1':
+ resolution: {integrity: sha512-UT/Jrhw57xg4ILHLFnzFpPDlMbcdEicaAtjPQpbj9wa8T4r5KVWCimHcL/460g8Ht0DMxDyjsLgiWSkVjnwPFg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-import-attributes@7.27.1':
+ resolution: {integrity: sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-jsx@7.27.1':
+ resolution: {integrity: sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-typescript@7.27.1':
+ resolution: {integrity: sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-syntax-unicode-sets-regex@7.18.6':
+ resolution: {integrity: sha512-727YkEAPwSIQTv5im8QHz3upqp92JTWhidIC81Tdx4VJYIte/VndKf1qKrfnnhPLiPghStWfvC/iFaMCQu7Nqg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-arrow-functions@7.27.1':
+ resolution: {integrity: sha512-8Z4TGic6xW70FKThA5HYEKKyBpOOsucTOD1DjU3fZxDg+K3zBJcXMFnt/4yQiZnf5+MiOMSXQ9PaEK/Ilh1DeA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-async-generator-functions@7.28.0':
+ resolution: {integrity: sha512-BEOdvX4+M765icNPZeidyADIvQ1m1gmunXufXxvRESy/jNNyfovIqUyE7MVgGBjWktCoJlzvFA1To2O4ymIO3Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-async-to-generator@7.27.1':
+ resolution: {integrity: sha512-NREkZsZVJS4xmTr8qzE5y8AfIPqsdQfRuUiLRTEzb7Qii8iFWCyDKaUV2c0rCuh4ljDZ98ALHP/PetiBV2nddA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-block-scoped-functions@7.27.1':
+ resolution: {integrity: sha512-cnqkuOtZLapWYZUYM5rVIdv1nXYuFVIltZ6ZJ7nIj585QsjKM5dhL2Fu/lICXZ1OyIAFc7Qy+bvDAtTXqGrlhg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-block-scoping@7.28.4':
+ resolution: {integrity: sha512-1yxmvN0MJHOhPVmAsmoW5liWwoILobu/d/ShymZmj867bAdxGbehIrew1DuLpw2Ukv+qDSSPQdYW1dLNE7t11A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-class-properties@7.27.1':
+ resolution: {integrity: sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-class-static-block@7.28.3':
+ resolution: {integrity: sha512-LtPXlBbRoc4Njl/oh1CeD/3jC+atytbnf/UqLoqTDcEYGUPj022+rvfkbDYieUrSj3CaV4yHDByPE+T2HwfsJg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.12.0
+
+ '@babel/plugin-transform-classes@7.28.4':
+ resolution: {integrity: sha512-cFOlhIYPBv/iBoc+KS3M6et2XPtbT2HiCRfBXWtfpc9OAyostldxIf9YAYB6ypURBBbx+Qv6nyrLzASfJe+hBA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-computed-properties@7.27.1':
+ resolution: {integrity: sha512-lj9PGWvMTVksbWiDT2tW68zGS/cyo4AkZ/QTp0sQT0mjPopCmrSkzxeXkznjqBxzDI6TclZhOJbBmbBLjuOZUw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-destructuring@7.28.0':
+ resolution: {integrity: sha512-v1nrSMBiKcodhsyJ4Gf+Z0U/yawmJDBOTpEB3mcQY52r9RIyPneGyAS/yM6seP/8I+mWI3elOMtT5dB8GJVs+A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-dotall-regex@7.27.1':
+ resolution: {integrity: sha512-gEbkDVGRvjj7+T1ivxrfgygpT7GUd4vmODtYpbs0gZATdkX8/iSnOtZSxiZnsgm1YjTgjI6VKBGSJJevkrclzw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-duplicate-keys@7.27.1':
+ resolution: {integrity: sha512-MTyJk98sHvSs+cvZ4nOauwTTG1JeonDjSGvGGUNHreGQns+Mpt6WX/dVzWBHgg+dYZhkC4X+zTDfkTU+Vy9y7Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1':
+ resolution: {integrity: sha512-hkGcueTEzuhB30B3eJCbCYeCaaEQOmQR0AdvzpD4LoN0GXMWzzGSuRrxR2xTnCrvNbVwK9N6/jQ92GSLfiZWoQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-dynamic-import@7.27.1':
+ resolution: {integrity: sha512-MHzkWQcEmjzzVW9j2q8LGjwGWpG2mjwaaB0BNQwst3FIjqsg8Ct/mIZlvSPJvfi9y2AC8mi/ktxbFVL9pZ1I4A==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-explicit-resource-management@7.28.0':
+ resolution: {integrity: sha512-K8nhUcn3f6iB+P3gwCv/no7OdzOZQcKchW6N389V6PD8NUWKZHzndOd9sPDVbMoBsbmjMqlB4L9fm+fEFNVlwQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-exponentiation-operator@7.27.1':
+ resolution: {integrity: sha512-uspvXnhHvGKf2r4VVtBpeFnuDWsJLQ6MF6lGJLC89jBR1uoVeqM416AZtTuhTezOfgHicpJQmoD5YUakO/YmXQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-export-namespace-from@7.27.1':
+ resolution: {integrity: sha512-tQvHWSZ3/jH2xuq/vZDy0jNn+ZdXJeM8gHvX4lnJmsc3+50yPlWdZXIc5ay+umX+2/tJIqHqiEqcJvxlmIvRvQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-for-of@7.27.1':
+ resolution: {integrity: sha512-BfbWFFEJFQzLCQ5N8VocnCtA8J1CLkNTe2Ms2wocj75dd6VpiqS5Z5quTYcUoo4Yq+DN0rtikODccuv7RU81sw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-function-name@7.27.1':
+ resolution: {integrity: sha512-1bQeydJF9Nr1eBCMMbC+hdwmRlsv5XYOMu03YSWFwNs0HsAmtSxxF1fyuYPqemVldVyFmlCU7w8UE14LupUSZQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-json-strings@7.27.1':
+ resolution: {integrity: sha512-6WVLVJiTjqcQauBhn1LkICsR2H+zm62I3h9faTDKt1qP4jn2o72tSvqMwtGFKGTpojce0gJs+76eZ2uCHRZh0Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-literals@7.27.1':
+ resolution: {integrity: sha512-0HCFSepIpLTkLcsi86GG3mTUzxV5jpmbv97hTETW3yzrAij8aqlD36toB1D0daVFJM8NK6GvKO0gslVQmm+zZA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-logical-assignment-operators@7.27.1':
+ resolution: {integrity: sha512-SJvDs5dXxiae4FbSL1aBJlG4wvl594N6YEVVn9e3JGulwioy6z3oPjx/sQBO3Y4NwUu5HNix6KJ3wBZoewcdbw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-member-expression-literals@7.27.1':
+ resolution: {integrity: sha512-hqoBX4dcZ1I33jCSWcXrP+1Ku7kdqXf1oeah7ooKOIiAdKQ+uqftgCFNOSzA5AMS2XIHEYeGFg4cKRCdpxzVOQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-amd@7.27.1':
+ resolution: {integrity: sha512-iCsytMg/N9/oFq6n+gFTvUYDZQOMK5kEdeYxmxt91fcJGycfxVP9CnrxoliM0oumFERba2i8ZtwRUCMhvP1LnA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-commonjs@7.27.1':
+ resolution: {integrity: sha512-OJguuwlTYlN0gBZFRPqwOGNWssZjfIUdS7HMYtN8c1KmwpwHFBwTeFZrg9XZa+DFTitWOW5iTAG7tyCUPsCCyw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-systemjs@7.27.1':
+ resolution: {integrity: sha512-w5N1XzsRbc0PQStASMksmUeqECuzKuTJer7kFagK8AXgpCMkeDMO5S+aaFb7A51ZYDF7XI34qsTX+fkHiIm5yA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-modules-umd@7.27.1':
+ resolution: {integrity: sha512-iQBE/xC5BV1OxJbp6WG7jq9IWiD+xxlZhLrdwpPkTX3ydmXdvoCpyfJN7acaIBZaOqTfr76pgzqBJflNbeRK+w==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-named-capturing-groups-regex@7.27.1':
+ resolution: {integrity: sha512-SstR5JYy8ddZvD6MhV0tM/j16Qds4mIpJTOd1Yu9J9pJjH93bxHECF7pgtc28XvkzTD6Pxcm/0Z73Hvk7kb3Ng==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-new-target@7.27.1':
+ resolution: {integrity: sha512-f6PiYeqXQ05lYq3TIfIDu/MtliKUbNwkGApPUvyo6+tc7uaR4cPjPe7DFPr15Uyycg2lZU6btZ575CuQoYh7MQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-nullish-coalescing-operator@7.27.1':
+ resolution: {integrity: sha512-aGZh6xMo6q9vq1JGcw58lZ1Z0+i0xB2x0XaauNIUXd6O1xXc3RwoWEBlsTQrY4KQ9Jf0s5rgD6SiNkaUdJegTA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-numeric-separator@7.27.1':
+ resolution: {integrity: sha512-fdPKAcujuvEChxDBJ5c+0BTaS6revLV7CJL08e4m3de8qJfNIuCc2nc7XJYOjBoTMJeqSmwXJ0ypE14RCjLwaw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-object-rest-spread@7.28.4':
+ resolution: {integrity: sha512-373KA2HQzKhQCYiRVIRr+3MjpCObqzDlyrM6u4I201wL8Mp2wHf7uB8GhDwis03k2ti8Zr65Zyyqs1xOxUF/Ew==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-object-super@7.27.1':
+ resolution: {integrity: sha512-SFy8S9plRPbIcxlJ8A6mT/CxFdJx/c04JEctz4jf8YZaVS2px34j7NXRrlGlHkN/M2gnpL37ZpGRGVFLd3l8Ng==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-optional-catch-binding@7.27.1':
+ resolution: {integrity: sha512-txEAEKzYrHEX4xSZN4kJ+OfKXFVSWKB2ZxM9dpcE3wT7smwkNmXo5ORRlVzMVdJbD+Q8ILTgSD7959uj+3Dm3Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-optional-chaining@7.27.1':
+ resolution: {integrity: sha512-BQmKPPIuc8EkZgNKsv0X4bPmOoayeu4F1YCwx2/CfmDSXDbp7GnzlUH+/ul5VGfRg1AoFPsrIThlEBj2xb4CAg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-parameters@7.27.7':
+ resolution: {integrity: sha512-qBkYTYCb76RRxUM6CcZA5KRu8K4SM8ajzVeUgVdMVO9NN9uI/GaVmBg/WKJJGnNokV9SY8FxNOVWGXzqzUidBg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-private-methods@7.27.1':
+ resolution: {integrity: sha512-10FVt+X55AjRAYI9BrdISN9/AQWHqldOeZDUoLyif1Kn05a56xVBXb8ZouL8pZ9jem8QpXaOt8TS7RHUIS+GPA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-private-property-in-object@7.27.1':
+ resolution: {integrity: sha512-5J+IhqTi1XPa0DXF83jYOaARrX+41gOewWbkPyjMNRDqgOCqdffGh8L3f/Ek5utaEBZExjSAzcyjmV9SSAWObQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-property-literals@7.27.1':
+ resolution: {integrity: sha512-oThy3BCuCha8kDZ8ZkgOg2exvPYUlprMukKQXI1r1pJ47NCvxfkEy8vK+r/hT9nF0Aa4H1WUPZZjHTFtAhGfmQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-constant-elements@7.27.1':
+ resolution: {integrity: sha512-edoidOjl/ZxvYo4lSBOQGDSyToYVkTAwyVoa2tkuYTSmjrB1+uAedoL5iROVLXkxH+vRgA7uP4tMg2pUJpZ3Ug==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-display-name@7.28.0':
+ resolution: {integrity: sha512-D6Eujc2zMxKjfa4Zxl4GHMsmhKKZ9VpcqIchJLvwTxad9zWIYulwYItBovpDOoNLISpcZSXoDJ5gaGbQUDqViA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx-development@7.27.1':
+ resolution: {integrity: sha512-ykDdF5yI4f1WrAolLqeF3hmYU12j9ntLQl/AOG1HAS21jxyg1Q0/J/tpREuYLfatGdGmXp/3yS0ZA76kOlVq9Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-jsx@7.27.1':
+ resolution: {integrity: sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-react-pure-annotations@7.27.1':
+ resolution: {integrity: sha512-JfuinvDOsD9FVMTHpzA/pBLisxpv1aSf+OIV8lgH3MuWrks19R27e6a6DipIg4aX1Zm9Wpb04p8wljfKrVSnPA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-regenerator@7.28.4':
+ resolution: {integrity: sha512-+ZEdQlBoRg9m2NnzvEeLgtvBMO4tkFBw5SQIUgLICgTrumLoU7lr+Oghi6km2PFj+dbUt2u1oby2w3BDO9YQnA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-regexp-modifiers@7.27.1':
+ resolution: {integrity: sha512-TtEciroaiODtXvLZv4rmfMhkCv8jx3wgKpL68PuiPh2M4fvz5jhsA7697N1gMvkvr/JTF13DrFYyEbY9U7cVPA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/plugin-transform-reserved-words@7.27.1':
+ resolution: {integrity: sha512-V2ABPHIJX4kC7HegLkYoDpfg9PVmuWy/i6vUM5eGK22bx4YVFD3M5F0QQnWQoDs6AGsUWTVOopBiMFQgHaSkVw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-shorthand-properties@7.27.1':
+ resolution: {integrity: sha512-N/wH1vcn4oYawbJ13Y/FxcQrWk63jhfNa7jef0ih7PHSIHX2LB7GWE1rkPrOnka9kwMxb6hMl19p7lidA+EHmQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-spread@7.27.1':
+ resolution: {integrity: sha512-kpb3HUqaILBJcRFVhFUs6Trdd4mkrzcGXss+6/mxUd273PfbWqSDHRzMT2234gIg2QYfAjvXLSquP1xECSg09Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-sticky-regex@7.27.1':
+ resolution: {integrity: sha512-lhInBO5bi/Kowe2/aLdBAawijx+q1pQzicSgnkB6dUPc1+RC8QmJHKf2OjvU+NZWitguJHEaEmbV6VWEouT58g==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-template-literals@7.27.1':
+ resolution: {integrity: sha512-fBJKiV7F2DxZUkg5EtHKXQdbsbURW3DZKQUWphDum0uRP6eHGGa/He9mc0mypL680pb+e/lDIthRohlv8NCHkg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typeof-symbol@7.27.1':
+ resolution: {integrity: sha512-RiSILC+nRJM7FY5srIyc4/fGIwUhyDuuBSdWn4y6yT6gm652DpCHZjIipgn6B7MQ1ITOUnAKWixEUjQRIBIcLw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-typescript@7.28.0':
+ resolution: {integrity: sha512-4AEiDEBPIZvLQaWlc9liCavE0xRM0dNca41WtBeM3jgFptfUOSG9z0uteLhq6+3rq+WB6jIvUwKDTpXEHPJ2Vg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-escapes@7.27.1':
+ resolution: {integrity: sha512-Ysg4v6AmF26k9vpfFuTZg8HRfVWzsh1kVfowA23y9j/Gu6dOuahdUVhkLqpObp3JIv27MLSii6noRnuKN8H0Mg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-property-regex@7.27.1':
+ resolution: {integrity: sha512-uW20S39PnaTImxp39O5qFlHLS9LJEmANjMG7SxIhap8rCHqu0Ik+tLEPX5DKmHn6CsWQ7j3lix2tFOa5YtL12Q==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-regex@7.27.1':
+ resolution: {integrity: sha512-xvINq24TRojDuyt6JGtHmkVkrfVV3FPT16uytxImLeBZqW3/H52yN+kM1MGuyPkIQxrzKwPHs5U/MP3qKyzkGw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/plugin-transform-unicode-sets-regex@7.27.1':
+ resolution: {integrity: sha512-EtkOujbc4cgvb0mlpQefi4NTPBzhSIevblFevACNLUspmrALgmEBdL/XfnyyITfd8fKBZrZys92zOWcik7j9Tw==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/preset-env@7.28.3':
+ resolution: {integrity: sha512-ROiDcM+GbYVPYBOeCR6uBXKkQpBExLl8k9HO1ygXEyds39j+vCCsjmj7S8GOniZQlEs81QlkdJZe76IpLSiqpg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/preset-modules@0.1.6-no-external-plugins':
+ resolution: {integrity: sha512-HrcgcIESLm9aIR842yhJ5RWan/gebQUJ6E/E5+rf0y9o6oj7w0Br+sWuL6kEQ/o/AdfvR1Je9jG18/gnpwjEyA==}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0 || ^8.0.0-0 <8.0.0
+
+ '@babel/preset-react@7.27.1':
+ resolution: {integrity: sha512-oJHWh2gLhU9dW9HHr42q0cI0/iHHXTLGe39qvpAZZzagHy0MzYLCnCVV0symeRvzmjHyVU7mw2K06E6u/JwbhA==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/preset-typescript@7.27.1':
+ resolution: {integrity: sha512-l7WfQfX0WK4M0v2RudjuQK4u99BS6yLHYEmdtVPP7lKV013zr9DygFuWNlnbvQ9LR+LS0Egz/XAvGx5U9MX0fQ==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@babel/template@7.27.2':
+ resolution: {integrity: sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.28.4':
+ resolution: {integrity: sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.28.4':
+ resolution: {integrity: sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==}
+ engines: {node: '>=6.9.0'}
+
+ '@csstools/color-helpers@5.1.0':
+ resolution: {integrity: sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==}
+ engines: {node: '>=18'}
+
+ '@csstools/css-calc@2.1.4':
+ resolution: {integrity: sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-color-parser@3.1.0':
+ resolution: {integrity: sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-parser-algorithms': ^3.0.5
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5':
+ resolution: {integrity: sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ '@csstools/css-tokenizer': ^3.0.4
+
+ '@csstools/css-syntax-patches-for-csstree@1.0.14':
+ resolution: {integrity: sha512-zSlIxa20WvMojjpCSy8WrNpcZ61RqfTfX3XTaOeVlGJrt/8HF3YbzgFZa01yTbT4GWQLwfTcC3EB8i3XnB647Q==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ postcss: ^8.4
+
+ '@csstools/css-tokenizer@3.0.4':
+ resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==}
+ engines: {node: '>=18'}
+
+ '@emnapi/core@1.5.0':
+ resolution: {integrity: sha512-sbP8GzB1WDzacS8fgNPpHlp6C9VZe+SJP3F90W9rLemaQj2PzIuTEl1qDOYQf58YIpyjViI24y9aPWCjEzY2cg==}
+
+ '@emnapi/runtime@1.5.0':
+ resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==}
+
+ '@emnapi/wasi-threads@1.1.0':
+ resolution: {integrity: sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==}
+
+ '@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.1':
+ resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.21.0':
+ resolution: {integrity: sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.3.1':
+ resolution: {integrity: sha512-xR93k9WhrDYpXHORXpxVL5oHj3Era7wo6k/Wd8/IsQNnZUTzkGS29lyn3nAT05v6ltUuTFVCCYDEGfy2Or/sPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.15.2':
+ resolution: {integrity: sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==}
+ 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.36.0':
+ resolution: {integrity: sha512-uhCbYtYynH30iZErszX78U+nR3pJU3RHGQ57NXy5QupD4SBVwDeU8TNBy+MjMngc1UyIW9noKqsRqfjQTBU2dw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.6':
+ resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.3.5':
+ resolution: {integrity: sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==}
+ 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.27.16':
+ resolution: {integrity: sha512-9O8N4SeG2z++TSM8QA/KTeKFBVCNEz/AGS7gWPJf6KFRzmRWixFRnCnkPHRDwSVZW6QPDO6uT0P2SpWNKCc9/g==}
+ peerDependencies:
+ react: '>=17.0.0'
+ react-dom: '>=17.0.0'
+
+ '@floating-ui/utils@0.2.10':
+ resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
+
+ '@hookform/resolvers@5.2.2':
+ resolution: {integrity: sha512-A/IxlMLShx3KjV/HeTcTfaMxdwy690+L/ZADoeaTltLx+CVuzkeVIPuybK3jrRfw7YZnmdKsVVHAlEPIAEUNlA==}
+ peerDependencies:
+ react-hook-form: ^7.55.0
+
+ '@humanfs/core@0.19.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]
+
+ '@isaacs/fs-minipass@4.0.1':
+ resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+ engines: {node: '>=18.0.0'}
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@0.2.12':
+ resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==}
+
+ '@next/env@16.0.10':
+ resolution: {integrity: sha512-8tuaQkyDVgeONQ1MeT9Mkk8pQmZapMKFh5B+OrFUlG3rVmYTXcXlBetBgTurKXGaIZvkoqRT9JL5K3phXcgang==}
+
+ '@next/eslint-plugin-next@15.5.4':
+ resolution: {integrity: sha512-SR1vhXNNg16T4zffhJ4TS7Xn7eq4NfKfcOsRwea7RIAHrjRpI9ALYbamqIJqkAhowLlERffiwk0FMvTLNdnVtw==}
+
+ '@next/swc-darwin-arm64@16.0.10':
+ resolution: {integrity: sha512-4XgdKtdVsaflErz+B5XeG0T5PeXKDdruDf3CRpnhN+8UebNa5N2H58+3GDgpn/9GBurrQ1uWW768FfscwYkJRg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@16.0.10':
+ resolution: {integrity: sha512-spbEObMvRKkQ3CkYVOME+ocPDFo5UqHb8EMTS78/0mQ+O1nqE8toHJVioZo4TvebATxgA8XMTHHrScPrn68OGw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@16.0.10':
+ resolution: {integrity: sha512-uQtWE3X0iGB8apTIskOMi2w/MKONrPOUCi5yLO+v3O8Mb5c7K4Q5KD1jvTpTF5gJKa3VH/ijKjKUq9O9UhwOYw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-arm64-musl@16.0.10':
+ resolution: {integrity: sha512-llA+hiDTrYvyWI21Z0L1GiXwjQaanPVQQwru5peOgtooeJ8qx3tlqRV2P7uH2pKQaUfHxI/WVarvI5oYgGxaTw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@next/swc-linux-x64-gnu@16.0.10':
+ resolution: {integrity: sha512-AK2q5H0+a9nsXbeZ3FZdMtbtu9jxW4R/NgzZ6+lrTm3d6Zb7jYrWcgjcpM1k8uuqlSy4xIyPR2YiuUr+wXsavA==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-linux-x64-musl@16.0.10':
+ resolution: {integrity: sha512-1TDG9PDKivNw5550S111gsO4RGennLVl9cipPhtkXIFVwo31YZ73nEbLjNC8qG3SgTz/QZyYyaFYMeY4BKZR/g==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@next/swc-win32-arm64-msvc@16.0.10':
+ resolution: {integrity: sha512-aEZIS4Hh32xdJQbHz121pyuVZniSNoqDVx1yIr2hy+ZwJGipeqnMZBJHyMxv2tiuAXGx6/xpTcQJ6btIiBjgmg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@16.0.10':
+ resolution: {integrity: sha512-E+njfCoFLb01RAFEnGZn6ERoOqhK1Gl3Lfz1Kjnj0Ulfu7oJbuMyvBKNj/bw8XZnenHDASlygTjZICQW+rYW1Q==}
+ 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'}
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.1':
+ resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
+ engines: {node: '>= 10.0.0'}
+
+ '@remirror/core-constants@3.0.0':
+ resolution: {integrity: sha512-42aWfPrimMfDKDi4YegyS7x+/0tlzaqwPQCULLanv3DMIlu96KTJR0fM5isWX2UViOqlGnX6YFgqWepcX+XMNg==}
+
+ '@rtsao/scc@1.1.0':
+ resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+
+ '@rushstack/eslint-patch@1.12.0':
+ resolution: {integrity: sha512-5EwMtOqvJMMa3HbmxLlF74e+3/HhwBTMcvt3nqVJgGCozO6hzIPOBlwm8mGVNR9SN2IJpxSnlxczyDjcn7qIyw==}
+
+ '@standard-schema/utils@0.3.0':
+ resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==}
+
+ '@svgr/babel-plugin-add-jsx-attribute@8.0.0':
+ resolution: {integrity: sha512-b9MIk7yhdS1pMCZM8VeNfUlSKVRhsHZNMl5O9SfaX0l0t5wjdgu4IDzGB8bpnGBBOjGST3rRFVsaaEtI4W6f7g==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-remove-jsx-attribute@8.0.0':
+ resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0':
+ resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0':
+ resolution: {integrity: sha512-KVQ+PtIjb1BuYT3ht8M5KbzWBhdAjjUPdlMtpuw/VjT8coTrItWX6Qafl9+ji831JaJcu6PJNKCV0bp01lBNzQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-svg-dynamic-title@8.0.0':
+ resolution: {integrity: sha512-omNiKqwjNmOQJ2v6ge4SErBbkooV2aAWwaPFs2vUY7p7GhVkzRkJ00kILXQvRhA6miHnNpXv7MRnnSjdRjK8og==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-svg-em-dimensions@8.0.0':
+ resolution: {integrity: sha512-mURHYnu6Iw3UBTbhGwE/vsngtCIbHE43xCRK7kCw4t01xyGqb2Pd+WXekRRoFOBIY29ZoOhUCTEweDMdrjfi9g==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-transform-react-native-svg@8.1.0':
+ resolution: {integrity: sha512-Tx8T58CHo+7nwJ+EhUwx3LfdNSG9R2OKfaIXXs5soiy5HtgoAEkDay9LIimLOcG8dJQH1wPZp/cnAv6S9CrR1Q==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-plugin-transform-svg-component@8.0.0':
+ resolution: {integrity: sha512-DFx8xa3cZXTdb/k3kfPeaixecQLgKh5NVBMwD0AQxOzcZawK4oo1Jh9LbrcACUivsCA7TLG8eeWgrDXjTMhRmw==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/babel-preset@8.1.0':
+ resolution: {integrity: sha512-7EYDbHE7MxHpv4sxvnVPngw5fuR6pw79SkcrILHJ/iMpuKySNCl5W1qcwPEpU+LgyRXOaAFgH0KhwD18wwg6ug==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@babel/core': ^7.0.0-0
+
+ '@svgr/cli@8.1.0':
+ resolution: {integrity: sha512-SnlaLspB610XFXvs3PmhzViHErsXp0yIy4ERyZlHDlO1ro2iYtHMWYk2mztdLD/lBjiA4ZXe4RePON3qU/Tc4A==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ '@svgr/core@8.1.0':
+ resolution: {integrity: sha512-8QqtOQT5ACVlmsvKOJNEaWmRPmcojMOzCz4Hs2BGG/toAp/K38LcsMRyLp349glq5AzJbCEeimEoxaX6v/fLrA==}
+ engines: {node: '>=14'}
+
+ '@svgr/hast-util-to-babel-ast@8.0.0':
+ resolution: {integrity: sha512-EbDKwO9GpfWP4jN9sGdYwPBU0kdomaPIL2Eu4YwmgP+sJeXT+L7bMwJUBnhzfH8Q2qMBqZ4fJwpCyYsAN3mt2Q==}
+ engines: {node: '>=14'}
+
+ '@svgr/plugin-jsx@8.1.0':
+ resolution: {integrity: sha512-0xiIyBsLlr8quN+WyuxooNW9RJ0Dpr8uOnH/xrCVO8GLUcwHISwj1AG0k+LFzteTkAA0GbX0kj9q6Dk70PTiPA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@svgr/core': '*'
+
+ '@svgr/plugin-prettier@8.1.0':
+ resolution: {integrity: sha512-o4/uFI8G64tAjBZ4E7gJfH+VP7Qi3T0+M4WnIsP91iFnGPqs5WvPDkpZALXPiyWEtzfYs1Rmwy1Zdfu8qoZuKw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@svgr/core': '*'
+
+ '@svgr/plugin-svgo@8.1.0':
+ resolution: {integrity: sha512-Ywtl837OGO9pTLIN/onoWLmDQ4zFUycI1g76vuKGEz6evR/ZTJlJuz3G/fIkb6OVBJ2g0o6CGJzaEjfmEo3AHA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@svgr/core': '*'
+
+ '@svgr/webpack@8.1.0':
+ resolution: {integrity: sha512-LnhVjMWyMQV9ZmeEy26maJk+8HTIbd59cH4F2MJ439k9DqejRisfFNGAPvRYlKETuh9LrImlS8aKsBgKjMA8WA==}
+ engines: {node: '>=14'}
+
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
+ '@tailwindcss/node@4.1.13':
+ resolution: {integrity: sha512-eq3ouolC1oEFOAvOMOBAmfCIqZBJuvWvvYWh5h5iOYfe1HFC6+GZ6EIL0JdM3/niGRJmnrOc+8gl9/HGUaaptw==}
+
+ '@tailwindcss/oxide-android-arm64@4.1.13':
+ resolution: {integrity: sha512-BrpTrVYyejbgGo57yc8ieE+D6VT9GOgnNdmh5Sac6+t0m+v+sKQevpFVpwX3pBrM2qKrQwJ0c5eDbtjouY/+ew==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.13':
+ resolution: {integrity: sha512-YP+Jksc4U0KHcu76UhRDHq9bx4qtBftp9ShK/7UGfq0wpaP96YVnnjFnj3ZFrUAjc5iECzODl/Ts0AN7ZPOANQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.1.13':
+ resolution: {integrity: sha512-aAJ3bbwrn/PQHDxCto9sxwQfT30PzyYJFG0u/BWZGeVXi5Hx6uuUOQEI2Fa43qvmUjTRQNZnGqe9t0Zntexeuw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.13':
+ resolution: {integrity: sha512-Wt8KvASHwSXhKE/dJLCCWcTSVmBj3xhVhp/aF3RpAhGeZ3sVo7+NTfgiN8Vey/Fi8prRClDs6/f0KXPDTZE6nQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13':
+ resolution: {integrity: sha512-mbVbcAsW3Gkm2MGwA93eLtWrwajz91aXZCNSkGTx/R5eb6KpKD5q8Ueckkh9YNboU8RH7jiv+ol/I7ZyQ9H7Bw==}
+ engines: {node: '>= 10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.13':
+ resolution: {integrity: sha512-wdtfkmpXiwej/yoAkrCP2DNzRXCALq9NVLgLELgLim1QpSfhQM5+ZxQQF8fkOiEpuNoKLp4nKZ6RC4kmeFH0HQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.13':
+ resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.13':
+ resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.13':
+ resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.13':
+ resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==}
+ 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.13':
+ resolution: {integrity: sha512-dziTNeQXtoQ2KBXmrjCxsuPk3F3CQ/yb7ZNZNA+UkNTeiTGgfeh+gH5Pi7mRncVgcPD2xgHvkFCh/MhZWSgyQg==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.13':
+ resolution: {integrity: sha512-3+LKesjXydTkHk5zXX01b5KMzLV1xl2mcktBJkje7rhFUpUlYJy7IMOLqjIRQncLTa1WZZiFY/foAeB5nmaiTw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.1.13':
+ resolution: {integrity: sha512-CPgsM1IpGRa880sMbYmG1s4xhAy3xEt1QULgTJGQmZUeNgXFR7s1YxYygmJyBGtou4SyEosGAGEeYqY7R53bIA==}
+ engines: {node: '>= 10'}
+
+ '@tailwindcss/postcss@4.1.13':
+ resolution: {integrity: sha512-HLgx6YSFKJT7rJqh9oJs/TkBFhxuMOfUKSBEPYwV+t78POOBsdQ7crhZLzwcH3T0UyUuOzU/GK5pk5eKr3wCiQ==}
+
+ '@tailwindcss/typography@0.5.19':
+ resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==}
+ peerDependencies:
+ tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1'
+
+ '@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
+
+ '@tiptap/core@3.7.2':
+ resolution: {integrity: sha512-fJwNpTx0aq4UU0HNkxPvPYfNBcTHQ/q5xBUdOB5Mgu6clwGES38jVsNNSudB8g53APUmJIS+2fJbkxl3V+0jww==}
+ peerDependencies:
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/extension-blockquote@3.7.2':
+ resolution: {integrity: sha512-8rNDh1E1ratex9KicvNNnjJGtF313Kpf5hXHOUcIm8FQwvA/0Tu6jq7r6VgESMyo95R3EmzRpnCYQef+zDm6OQ==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-bold@3.7.2':
+ resolution: {integrity: sha512-bwCn9lQEXnEi7LfIx3G/oaH4I0ZapAgrHzLCNJH/tNgRKVWym1H1Oa8PlkiFDbalWOdUkbgeAUqUaIB13k408Q==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-bubble-menu@3.7.2':
+ resolution: {integrity: sha512-rCJu/X7sZEYWkOwLO342JP06f4giVBECPzr/SzG/fQdAidPW96eilPk3L82w5j24kS9odTlxSLlFlIf6UZ2b9w==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/extension-bullet-list@3.7.2':
+ resolution: {integrity: sha512-OHYYXKjmxisLQws0tW8Dz14PcyIJmaed7eypZvIm/R3hxa/7lJY/2EM/Ti5g/w1U8WPBEH1hX3icRtiulserKw==}
+ peerDependencies:
+ '@tiptap/extension-list': ^3.7.2
+
+ '@tiptap/extension-code-block@3.7.2':
+ resolution: {integrity: sha512-TfixutvvbGCrSSCsfDK/PBm6A5FIzcPTSVDrmmsiAfqldj/Woy1T42dads+wv9SjKG06GlWDwYtDGAk2Uun8NA==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/extension-code@3.7.2':
+ resolution: {integrity: sha512-J8FaCiKJJnHvQiPcbfbUtc5RNmGx/Gui/K5CDMPc17jhCiQ9JhR9idRPREV24Z2t7GujWX7LG6ZDDR82pSns+g==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-document@3.7.2':
+ resolution: {integrity: sha512-OrHl402v2FWCUKR1Xi5MTNBAkKYQh7mtpw/WlJDFnk5z1qHLqz4UIcbGilDYzVPrNUZPhA1p3c+V5UUVUFzUfg==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-dropcursor@3.7.2':
+ resolution: {integrity: sha512-79y6M9pJYwqcqBHIWoomfptJp0QB/TP3Y+2NOL09sMNeSdUgmz5pCVObA4H48YMkoB0EcUtux2IUOM66e4nsJA==}
+ peerDependencies:
+ '@tiptap/extensions': ^3.7.2
+
+ '@tiptap/extension-floating-menu@3.7.2':
+ resolution: {integrity: sha512-g19ratrXlplYDS29VLQa1y/IM/ro0UFhSS4fQokiQKkazwnA1ZVnebjw8ERYg5lkMm/hiImqstpgdO0LtoivvQ==}
+ peerDependencies:
+ '@floating-ui/dom': ^1.0.0
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/extension-gapcursor@3.7.2':
+ resolution: {integrity: sha512-vCLo2dL2SfeWjh/gJKDiu0/fz6OF7obGTJvHg/yStkoUqlAEiwKoyHP/NXeTGYJMzZzUi0kY9DtTEJdGFvphuQ==}
+ peerDependencies:
+ '@tiptap/extensions': ^3.7.2
+
+ '@tiptap/extension-hard-break@3.7.2':
+ resolution: {integrity: sha512-nNDo+5S1yRQ3JkBM+gwpEEVZ/Kw9qWoG/cpShyGYDHo1/y8MgO+VI0kSb/LuBTw7g+jmNXdf+ZaRRI/pXsUihg==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-heading@3.7.2':
+ resolution: {integrity: sha512-eH/G66FIRlTQz4MhEmlNNNQgVTxhoqlkyFzgeG5aipIplYOdYa5Y6Wl0NF4xqr1jAHGLAK6LaYS4FXp3TE7LyA==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-horizontal-rule@3.7.2':
+ resolution: {integrity: sha512-pN+1hJAVVP3uqtpZ5Rm7z5XUB/NGprK6wExJ04xG117E4rTVcaEb1FnMILY3J3A5XbdC3vHX+cblR8mOl1PAMw==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/extension-italic@3.7.2':
+ resolution: {integrity: sha512-1tfF37LvKgA5hg09UBgOjdMLNRb1C6keIOBF0r5oHKeWPYOf4z3j5IU9PsFUoOn53XRMb1aiD/TNbGPyoT3Fyw==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-link@3.7.2':
+ resolution: {integrity: sha512-9K54PxBiDSWAMfICqkb8jcQ6cL7vDAtjTk0zqBw4d+XuaUy0FC9QUdbx7r1Pkbf36K1/ApbvM9a7qpOirWk8Xw==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/extension-list-item@3.7.2':
+ resolution: {integrity: sha512-962TFsx4eF5NMyLVhGFGF/btt5j3MipPhDiUsxzBgnlW8o5OonVepb9cDrqpEDQ2/wLvheWnCKuvmG7umasldQ==}
+ peerDependencies:
+ '@tiptap/extension-list': ^3.7.2
+
+ '@tiptap/extension-list-keymap@3.7.2':
+ resolution: {integrity: sha512-1du9eo+NPIkuRT258yUn9bovhip556aJo/yDtRbswEVNScP1E8y/kFRWvw0HD7/YWcNqok1ZteoSwShWnKAXRQ==}
+ peerDependencies:
+ '@tiptap/extension-list': ^3.7.2
+
+ '@tiptap/extension-list@3.7.2':
+ resolution: {integrity: sha512-/tYHmEkOGcVweAc9ZgnAXkzua5aJfu7TjZcKTq5fmDt6x9MY1eY1+egS7D9hVR2sUSAC10VgXmYdYPDsKF3p2g==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/extension-ordered-list@3.7.2':
+ resolution: {integrity: sha512-Tu61/JXh1RRd3Kb+s7A7jmpnB+w1pqGSRfMXBtYHDHDIGyXu255ru7soX44lJfHGq/zYcTFSHGSsi8o23QONJg==}
+ peerDependencies:
+ '@tiptap/extension-list': ^3.7.2
+
+ '@tiptap/extension-paragraph@3.7.2':
+ resolution: {integrity: sha512-HmDuAixTcvP4A/v6OLkh/C6nB86i7/DRNswBf/Udak8TgWUIcSUK0iActxxm5+B3MZTSf3U87JzyI6IeuElLIQ==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-placeholder@3.7.2':
+ resolution: {integrity: sha512-YUr1rlxkgEBQDsMLpU8ruA4Uet37kXvwwFwIbDgaFd4NpfAD0fvX2zmPhHIBzsdH3e4V6eNp6IkmoYCWvugAAA==}
+ peerDependencies:
+ '@tiptap/extensions': ^3.7.2
+
+ '@tiptap/extension-strike@3.7.2':
+ resolution: {integrity: sha512-I1G+4vZbCBTpAMmyVwaO8cLBJgXEf1DyEzc0B+HhTJiBa9qA9OKgRQEGFgisxu1kggjbzB6+d0+taHfjsZC1SQ==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-text@3.7.2':
+ resolution: {integrity: sha512-sKaeGYNP1+bAe2rvmzWLW5qH9DsSFOJlOUEOFchR0OX0rC7bbGS6/KuyAq0w6UkL+cMJnDyAbv3KeD2WEA192w==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extension-underline@3.7.2':
+ resolution: {integrity: sha512-GDpUZllTD7uIdHjTzYJ6i4jUgCeviW40SCpLVVv1xH0gj1t1xu0Rnxmk+bXkF2XNe8jPXkMCgYNr6DR6eO8roQ==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+
+ '@tiptap/extensions@3.7.2':
+ resolution: {integrity: sha512-FaToSdU9fhQk2swkaXrAQNgdaE0dwLbUHcvilW5F4xTpQfZ3J535u5U2TUYf+f9KKSV5fTmD4QGNY9qxY7ihTg==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+
+ '@tiptap/pm@3.7.2':
+ resolution: {integrity: sha512-i2fvXDapwo/TWfHM6STYEbkYyF3qyfN6KEBKPrleX/Z80G5bLxom0gB79TsjLNxTLi6mdf0vTHgAcXMG1avc2g==}
+
+ '@tiptap/react@3.7.2':
+ resolution: {integrity: sha512-tka4ioSmsGI4TyGZ7jAUoIw8t8DVjr1It0B38vZVLqg8M/ZFgR1NkF50TJ6qAkhy8Uz12AO50so0v79tV2pmEA==}
+ peerDependencies:
+ '@tiptap/core': ^3.7.2
+ '@tiptap/pm': ^3.7.2
+ '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
+ '@types/react-dom': ^17.0.0 || ^18.0.0 || ^19.0.0
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ '@tiptap/starter-kit@3.7.2':
+ resolution: {integrity: sha512-43GwI+2Mtc/ci7J4eJOE02wZxp5KIsDTMMb0peNksPcEGaURGdDhav9zbAW24NRjRxU7Auk/zaQu9O8+ZE0v0A==}
+
+ '@trysound/sax@0.2.0':
+ resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
+ engines: {node: '>=10.13.0'}
+
+ '@tybys/wasm-util@0.10.1':
+ resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==}
+
+ '@types/axios@0.14.4':
+ resolution: {integrity: sha512-9JgOaunvQdsQ/qW2OPmE5+hCeUB52lQSolecrFrthct55QekhmXEwT203s20RL+UHtCQc15y3VXpby9E7Kkh/g==}
+ deprecated: This is a stub types definition. axios provides its own type definitions, so you do not need this installed.
+
+ '@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/linkify-it@5.0.0':
+ resolution: {integrity: sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==}
+
+ '@types/markdown-it@14.1.2':
+ resolution: {integrity: sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==}
+
+ '@types/mdurl@2.0.0':
+ resolution: {integrity: sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==}
+
+ '@types/node@20.19.18':
+ resolution: {integrity: sha512-KeYVbfnbsBCyKG8e3gmUqAfyZNcoj/qpEbHRkQkfZdKOBrU7QQ+BsTdfqLSWX9/m1ytYreMhpKvp+EZi3UFYAg==}
+
+ '@types/qs@6.14.0':
+ resolution: {integrity: sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==}
+
+ '@types/react-dom@19.1.9':
+ resolution: {integrity: sha512-qXRuZaOsAdXKFyOhRBg6Lqqc0yay13vN7KrIg4L7N4aaHN68ma9OK3NE1BoDFgFOTfM7zg+3/8+2n8rLUH3OKQ==}
+ peerDependencies:
+ '@types/react': ^19.0.0
+
+ '@types/react@19.1.16':
+ resolution: {integrity: sha512-WBM/nDbEZmDUORKnh5i1bTnAz6vTohUf9b8esSMu+b24+srbaxa04UbJgWx78CVfNXA20sNu0odEIluZDFdCog==}
+
+ '@types/trusted-types@2.0.7':
+ resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==}
+
+ '@types/use-sync-external-store@0.0.6':
+ resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==}
+
+ '@typescript-eslint/eslint-plugin@8.45.0':
+ resolution: {integrity: sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.45.0
+ eslint: ^8.57.0 || ^9.0.0
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/parser@8.45.0':
+ resolution: {integrity: sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==}
+ 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.45.0':
+ resolution: {integrity: sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/scope-manager@8.45.0':
+ resolution: {integrity: sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.45.0':
+ resolution: {integrity: sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/type-utils@8.45.0':
+ resolution: {integrity: sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==}
+ 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.45.0':
+ resolution: {integrity: sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.45.0':
+ resolution: {integrity: sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.0.0'
+
+ '@typescript-eslint/utils@8.45.0':
+ resolution: {integrity: sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==}
+ 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.45.0':
+ resolution: {integrity: sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==}
+ 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]
+
+ 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
+
+ agent-base@7.1.4:
+ resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==}
+ engines: {node: '>= 14'}
+
+ 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-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ array-buffer-byte-length@1.0.2:
+ resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
+ engines: {node: '>= 0.4'}
+
+ array-includes@3.1.9:
+ resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlast@1.2.5:
+ resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlastindex@1.2.6:
+ resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flat@1.3.3:
+ resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flatmap@1.3.3:
+ resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.tosorted@1.1.4:
+ resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
+ engines: {node: '>= 0.4'}
+
+ arraybuffer.prototype.slice@1.0.4:
+ resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
+ engines: {node: '>= 0.4'}
+
+ ast-types-flow@0.0.8:
+ resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
+
+ async-function@1.0.0:
+ resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
+ engines: {node: '>= 0.4'}
+
+ async-generator-function@1.0.0:
+ resolution: {integrity: sha512-+NAXNqgCrB95ya4Sr66i1CL2hqLVckAk7xwRYWdcm39/ELQ6YNn1aw5r0bdQtqNZgQpEWzc5yc/igXc7aL5SLA==}
+ 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.10.3:
+ resolution: {integrity: sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==}
+ 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'}
+
+ babel-plugin-polyfill-corejs2@0.4.14:
+ resolution: {integrity: sha512-Co2Y9wX854ts6U8gAAPXfn0GmAyctHuK8n0Yhfjd6t30g7yvKjspvvOo9yG+z52PZRgFErt7Ka2pYnXCjLKEpg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-plugin-polyfill-corejs3@0.13.0:
+ resolution: {integrity: sha512-U+GNwMdSFgzVmfhNm8GJUX88AadB3uo9KpJqS3FaqNIPKgySuvMb+bHPsOmmuWyIcuqZj/pzt1RUIUZns4y2+A==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ babel-plugin-polyfill-regenerator@0.6.5:
+ resolution: {integrity: sha512-ISqQ2frbiNU9vIJkzg7dlPpznPZ4jOiUQ1uSmB0fEHeowtN3COYRsXr/xexn64NpU13P06jc/L5TgiJXOgrbEg==}
+ peerDependencies:
+ '@babel/core': ^7.4.0 || ^8.0.0-0 <8.0.0
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ baseline-browser-mapping@2.8.13:
+ resolution: {integrity: sha512-7s16KR8io8nIBWQyCYhmFhd+ebIzb9VKTzki+wOJXHTxTnV6+mFGH3+Jwn1zoKaY9/H9T/0BcKCZnzXljPnpSQ==}
+ hasBin: true
+
+ bidi-js@1.0.3:
+ resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
+
+ boolbase@1.0.0:
+ resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==}
+
+ 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'}
+
+ browserslist@4.26.3:
+ resolution: {integrity: sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.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'}
+
+ camelcase@6.3.0:
+ resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+ engines: {node: '>=10'}
+
+ caniuse-lite@1.0.30001746:
+ resolution: {integrity: sha512-eA7Ys/DGw+pnkWWSE/id29f2IcPHVoE8wxtvE5JdvD2V28VTDPy1yEeo11Guz0sJ4ZeGRcm3uaTcAqK1LXaphA==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ chokidar@4.0.3:
+ resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==}
+ engines: {node: '>= 14.16.0'}
+
+ chownr@3.0.0:
+ resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+ engines: {node: '>=18'}
+
+ 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'}
+
+ commander@7.2.0:
+ resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==}
+ engines: {node: '>= 10'}
+
+ commander@9.5.0:
+ resolution: {integrity: sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==}
+ engines: {node: ^12.20.0 || >=14}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ core-js-compat@3.45.1:
+ resolution: {integrity: sha512-tqTt5T4PzsMIZ430XGviK4vzYSoeNJ6CXODi6c/voxOT6IZqBht5/EKaSNnYiEjjRYxjVz7DQIsOsY0XNi8PIA==}
+
+ cosmiconfig@8.3.6:
+ resolution: {integrity: sha512-kcZ6+W5QzcJ3P1Mt+83OUv/oHFqZHIx8DuxG6eZ5RGMERoLqp4BuGjhHLYGK+Kf5XVkQvqBSmAy/nGWN3qDgEA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ typescript: '>=4.9.5'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ crelt@1.0.6:
+ resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ css-select@5.2.2:
+ resolution: {integrity: sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==}
+
+ css-tree@2.2.1:
+ resolution: {integrity: sha512-OA0mILzGc1kCOCSJerOeqDxDQ4HOh+G8NbOJFOTgOCzpw7fCBubk0fEyxp8AgOL/jvLgYA/uV0cMbe43ElF1JA==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+ css-tree@2.3.1:
+ resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css-tree@3.1.0:
+ resolution: {integrity: sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0}
+
+ css-what@6.2.2:
+ resolution: {integrity: sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==}
+ engines: {node: '>= 6'}
+
+ cssesc@3.0.0:
+ resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
+ engines: {node: '>=4'}
+ hasBin: true
+
+ csso@5.0.5:
+ resolution: {integrity: sha512-0LrrStPOdJj+SPCCrGhzryycLjwcgUSHBtxNA8aIDxf0GLsRh1cKYhB00Gd1lDOS4yGH69+SNn13+TWbVHETFQ==}
+ engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0, npm: '>=7.0.0'}
+
+ cssstyle@5.3.1:
+ resolution: {integrity: sha512-g5PC9Aiph9eiczFpcgUhd9S4UUO3F+LHGRIi5NUMZ+4xtoIYbHNZwZnWA2JsFGe8OU8nl4WyaEFiZuGuxlutJQ==}
+ engines: {node: '>=20'}
+
+ csstype@3.1.3:
+ resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+
+ damerau-levenshtein@1.0.8:
+ resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
+
+ dashify@2.0.0:
+ resolution: {integrity: sha512-hpA5C/YrPjucXypHPPc0oJ1l9Hf6wWbiOL7Ik42cxnsUOhWiCB/fylKbKqqJalW9FgkNQCw16YO8uW9Hs0Iy1A==}
+ engines: {node: '>=4'}
+
+ data-urls@6.0.0:
+ resolution: {integrity: sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==}
+ engines: {node: '>=20'}
+
+ 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@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
+
+ decimal.js@10.6.0:
+ resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ deepmerge@4.3.1:
+ resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==}
+ engines: {node: '>=0.10.0'}
+
+ 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@1.0.3:
+ resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==}
+ engines: {node: '>=0.10'}
+ hasBin: true
+
+ detect-libc@2.1.1:
+ resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==}
+ engines: {node: '>=8'}
+
+ doctrine@2.1.0:
+ resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+ engines: {node: '>=0.10.0'}
+
+ dom-serializer@2.0.0:
+ resolution: {integrity: sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==}
+
+ domelementtype@2.3.0:
+ resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==}
+
+ domhandler@5.0.3:
+ resolution: {integrity: sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==}
+ engines: {node: '>= 4'}
+
+ dompurify@3.3.0:
+ resolution: {integrity: sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==}
+
+ domutils@3.2.2:
+ resolution: {integrity: sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==}
+
+ dot-case@3.0.4:
+ resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ electron-to-chromium@1.5.233:
+ resolution: {integrity: sha512-iUdTQSf7EFXsDdQsp8MwJz5SVk4APEFqXU/S47OtQ0YLqacSwPXdZ5vRlMX3neb07Cy2vgioNuRnWUXFwuslkg==}
+
+ 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'}
+
+ entities@4.5.0:
+ resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==}
+ engines: {node: '>=0.12'}
+
+ entities@6.0.1:
+ resolution: {integrity: sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==}
+ engines: {node: '>=0.12'}
+
+ error-ex@1.3.4:
+ resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==}
+
+ 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-object-atoms@1.1.1:
+ resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es-shim-unscopables@1.1.0:
+ resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
+ engines: {node: '>= 0.4'}
+
+ es-to-primitive@1.3.0:
+ resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
+ engines: {node: '>= 0.4'}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-config-next@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-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-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.36.0:
+ resolution: {integrity: sha512-hB4FIzXovouYzwzECDcUkJ4OcfOEkXTv2zRY6B9bkwjx/cprAq0uvm1nl7zvQ0/TsUk0zQiN4uPfJpB9m+rPMQ==}
+ 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'}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.1:
+ resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-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'}
+
+ 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
+
+ fs.realpath@1.0.0:
+ resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==}
+
+ 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.0:
+ resolution: {integrity: sha512-xPypGGincdfyl/AiSGa7GjXLkvld9V7GjZlowup9SHIJnQnHLFiLODCd/DqKOp0PBagbHJ68r1KJI9Mut7m4sA==}
+ engines: {node: '>= 0.4'}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-intrinsic@1.3.1:
+ resolution: {integrity: sha512-fk1ZVEeOX9hVZ6QzoBNEC55+Ucqg4sTVwrVuigZhuRPESVFpMyXnd3sbXvPOwp7Y9riVyANiqhEuRF0G1aVSeQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-symbol-description@1.1.0:
+ resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.10.1:
+ resolution: {integrity: sha512-auHyJ4AgMz7vgS8Hp3N6HXSmlMdUyhSUrfBF16w153rxtLIEOE+HGqaBppczZvnHLqQJfiHotCYpNhl0lUROFQ==}
+
+ 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'}
+
+ glob@8.1.0:
+ resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
+ engines: {node: '>=12'}
+ deprecated: Glob versions prior to v9 are no longer supported
+
+ 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'}
+
+ html-encoding-sniffer@4.0.0:
+ resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==}
+ engines: {node: '>=18'}
+
+ http-proxy-agent@7.0.2:
+ resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==}
+ engines: {node: '>= 14'}
+
+ https-proxy-agent@7.0.6:
+ resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==}
+ engines: {node: '>= 14'}
+
+ iconv-lite@0.6.3:
+ resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+ engines: {node: '>=0.10.0'}
+
+ 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'}
+
+ immutable@5.1.4:
+ resolution: {integrity: sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==}
+
+ 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'}
+
+ inflight@1.0.6:
+ resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==}
+ deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.
+
+ 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-arrayish@0.2.1:
+ resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==}
+
+ 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.0:
+ resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==}
+ 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-potential-custom-element-name@1.0.1:
+ resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==}
+
+ is-regex@1.2.1:
+ resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
+ engines: {node: '>= 0.4'}
+
+ is-set@2.0.3:
+ resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+ engines: {node: '>= 0.4'}
+
+ is-shared-array-buffer@1.0.4:
+ resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
+ engines: {node: '>= 0.4'}
+
+ is-string@1.1.1:
+ resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
+ engines: {node: '>= 0.4'}
+
+ is-symbol@1.1.1:
+ resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
+ engines: {node: '>= 0.4'}
+
+ is-typed-array@1.1.15:
+ resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+ engines: {node: '>= 0.4'}
+
+ is-weakmap@2.0.2:
+ resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
+ engines: {node: '>= 0.4'}
+
+ is-weakref@1.1.1:
+ resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
+ engines: {node: '>= 0.4'}
+
+ is-weakset@2.0.4:
+ resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
+ engines: {node: '>= 0.4'}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ isomorphic-dompurify@2.30.1:
+ resolution: {integrity: sha512-VJFbthRrns7BE+q3qSUJ5zxGNjuq4FqiaWXKCwnMoJbumnoQJoeOeOzP/oejKLPPtENckLWoDxGQiv5OkEFC+Q==}
+ engines: {node: '>=20.19.5'}
+
+ iterator.prototype@1.1.5:
+ resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
+ engines: {node: '>= 0.4'}
+
+ jiti@2.6.0:
+ resolution: {integrity: sha512-VXe6RjJkBPj0ohtqaO8vSWP3ZhAKo66fKrFNCll4BTcwljPLz03pCbaNKfzGP5MbrCYcbJ7v0nOYYwUzTEIdXQ==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@4.1.0:
+ resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==}
+ hasBin: true
+
+ jsdom@27.0.1:
+ resolution: {integrity: sha512-SNSQteBL1IlV2zqhwwolaG9CwhIhTvVHWg3kTss/cLE7H/X4644mtPQqYvCfsSrGQWt9hSZcgOXX8bOZaMN+kA==}
+ engines: {node: '>=20'}
+ peerDependencies:
+ canvas: ^3.0.0
+ peerDependenciesMeta:
+ canvas:
+ optional: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-parse-even-better-errors@2.3.1:
+ resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsx-ast-utils@3.3.5:
+ resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
+ engines: {node: '>=4.0'}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ language-subtag-registry@0.3.23:
+ resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
+
+ language-tags@1.0.9:
+ resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
+ engines: {node: '>=0.10'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lightningcss-darwin-arm64@1.30.1:
+ resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.30.1:
+ resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.30.1:
+ resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-linux-x64-musl@1.30.1:
+ resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.30.1:
+ resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+ engines: {node: '>= 12.0.0'}
+
+ lines-and-columns@1.2.4:
+ resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==}
+
+ linkify-it@5.0.0:
+ resolution: {integrity: sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==}
+
+ linkifyjs@4.3.2:
+ resolution: {integrity: sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.debounce@4.0.8:
+ resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==}
+
+ 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
+
+ lower-case@2.0.2:
+ resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==}
+
+ lru-cache@11.2.2:
+ resolution: {integrity: sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==}
+ engines: {node: 20 || >=22}
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ magic-string@0.30.19:
+ resolution: {integrity: sha512-2N21sPY9Ws53PZvsEpVtNuSW+ScYbQdp4b9qUaL+9QkHUrGFKo56Lg9Emg5s9V/qrtNBmiR01sYhUOwu3H+VOw==}
+
+ markdown-it@14.1.0:
+ resolution: {integrity: sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==}
+ hasBin: true
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ mdn-data@2.0.28:
+ resolution: {integrity: sha512-aylIc7Z9y4yzHYAJNuESG3hfhC+0Ibp/MAMiaOZgNv4pmEdFyfZhhhny4MNiAfWdBQ1RQ2mfDWmM1x8SvGyp8g==}
+
+ mdn-data@2.0.30:
+ resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==}
+
+ mdn-data@2.12.2:
+ resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
+
+ mdurl@2.0.0:
+ resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==}
+
+ 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@5.1.6:
+ resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==}
+ engines: {node: '>=10'}
+
+ 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==}
+
+ minipass@7.1.2:
+ resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
+ engines: {node: '>=16 || 14 >=14.17'}
+
+ minizlib@3.1.0:
+ resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
+ engines: {node: '>= 18'}
+
+ 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.3:
+ resolution: {integrity: sha512-uTp172LLXSxuSYHv/kou+f6KW3SMppU9ivthaVTXian9sOt3XM/zHYHpRZiLgQoxeWfYUnslNWQHF1+G71xcow==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ next@16.0.10:
+ resolution: {integrity: sha512-RtWh5PUgI+vxlV3HdR+IfWA1UUHu0+Ram/JBO4vWB54cVPentCD0e+lxyAYEsDTqGGMg7qpjhKh6dc6aW7W/sA==}
+ engines: {node: '>=20.9.0'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ babel-plugin-react-compiler: '*'
+ react: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ react-dom: ^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0
+ sass: ^1.3.0
+ peerDependenciesMeta:
+ '@opentelemetry/api':
+ optional: true
+ '@playwright/test':
+ optional: true
+ babel-plugin-react-compiler:
+ optional: true
+ sass:
+ optional: true
+
+ no-case@3.0.4:
+ resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==}
+
+ node-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ node-releases@2.0.23:
+ resolution: {integrity: sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==}
+
+ nth-check@2.1.1:
+ resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==}
+
+ 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'}
+
+ once@1.4.0:
+ resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ orderedmap@2.1.1:
+ resolution: {integrity: sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==}
+
+ own-keys@1.0.1:
+ resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+ engines: {node: '>= 0.4'}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ parse-json@5.2.0:
+ resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
+ engines: {node: '>=8'}
+
+ parse5@8.0.0:
+ resolution: {integrity: sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==}
+
+ 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==}
+
+ path-type@4.0.0:
+ resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==}
+ engines: {node: '>=8'}
+
+ 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-selector-parser@6.0.10:
+ resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
+ engines: {node: '>=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-plugin-tailwindcss@0.6.14:
+ resolution: {integrity: sha512-pi2e/+ZygeIqntN+vC573BcW5Cve8zUB0SSAGxqpB4f96boZF4M3phPVoOFCeypwkpRYdi7+jQ5YJJUwrkGUAg==}
+ engines: {node: '>=14.21.3'}
+ 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-import-sort: '*'
+ prettier-plugin-jsdoc: '*'
+ prettier-plugin-marko: '*'
+ prettier-plugin-multiline-arrays: '*'
+ prettier-plugin-organize-attributes: '*'
+ prettier-plugin-organize-imports: '*'
+ prettier-plugin-sort-imports: '*'
+ prettier-plugin-style-order: '*'
+ 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-import-sort:
+ 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-style-order:
+ optional: true
+ prettier-plugin-svelte:
+ optional: true
+
+ prettier@2.8.8:
+ resolution: {integrity: sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==}
+ engines: {node: '>=10.13.0'}
+ hasBin: true
+
+ prettier@3.6.2:
+ resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ prosemirror-changeset@2.3.1:
+ resolution: {integrity: sha512-j0kORIBm8ayJNl3zQvD1TTPHJX3g042et6y/KQhZhnPrruO8exkTgG8X+NRpj7kIyMMEx74Xb3DyMIBtO0IKkQ==}
+
+ prosemirror-collab@1.3.1:
+ resolution: {integrity: sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==}
+
+ prosemirror-commands@1.7.1:
+ resolution: {integrity: sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==}
+
+ prosemirror-dropcursor@1.8.2:
+ resolution: {integrity: sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==}
+
+ prosemirror-gapcursor@1.4.0:
+ resolution: {integrity: sha512-z00qvurSdCEWUIulij/isHaqu4uLS8r/Fi61IbjdIPJEonQgggbJsLnstW7Lgdk4zQ68/yr6B6bf7sJXowIgdQ==}
+
+ prosemirror-history@1.4.1:
+ resolution: {integrity: sha512-2JZD8z2JviJrboD9cPuX/Sv/1ChFng+xh2tChQ2X4bB2HeK+rra/bmJ3xGntCcjhOqIzSDG6Id7e8RJ9QPXLEQ==}
+
+ prosemirror-inputrules@1.5.1:
+ resolution: {integrity: sha512-7wj4uMjKaXWAQ1CDgxNzNtR9AlsuwzHfdFH1ygEHA2KHF2DOEaXl1CJfNPAKCg9qNEh4rum975QLaCiQPyY6Fw==}
+
+ prosemirror-keymap@1.2.3:
+ resolution: {integrity: sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==}
+
+ prosemirror-markdown@1.13.2:
+ resolution: {integrity: sha512-FPD9rHPdA9fqzNmIIDhhnYQ6WgNoSWX9StUZ8LEKapaXU9i6XgykaHKhp6XMyXlOWetmaFgGDS/nu/w9/vUc5g==}
+
+ prosemirror-menu@1.2.5:
+ resolution: {integrity: sha512-qwXzynnpBIeg1D7BAtjOusR+81xCp53j7iWu/IargiRZqRjGIlQuu1f3jFi+ehrHhWMLoyOQTSRx/IWZJqOYtQ==}
+
+ prosemirror-model@1.25.3:
+ resolution: {integrity: sha512-dY2HdaNXlARknJbrManZ1WyUtos+AP97AmvqdOQtWtrrC5g4mohVX5DTi9rXNFSk09eczLq9GuNTtq3EfMeMGA==}
+
+ prosemirror-schema-basic@1.2.4:
+ resolution: {integrity: sha512-ELxP4TlX3yr2v5rM7Sb70SqStq5NvI15c0j9j/gjsrO5vaw+fnnpovCLEGIcpeGfifkuqJwl4fon6b+KdrODYQ==}
+
+ prosemirror-schema-list@1.5.1:
+ resolution: {integrity: sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==}
+
+ prosemirror-state@1.4.3:
+ resolution: {integrity: sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==}
+
+ prosemirror-tables@1.8.1:
+ resolution: {integrity: sha512-DAgDoUYHCcc6tOGpLVPSU1k84kCUWTWnfWX3UDy2Delv4ryH0KqTD6RBI6k4yi9j9I8gl3j8MkPpRD/vWPZbug==}
+
+ prosemirror-trailing-node@3.0.0:
+ resolution: {integrity: sha512-xiun5/3q0w5eRnGYfNlW1uU9W6x5MoFKWwq/0TIRgt09lv7Hcser2QYV8t4muXbEr+Fwo0geYn79Xs4GKywrRQ==}
+ peerDependencies:
+ prosemirror-model: ^1.22.1
+ prosemirror-state: ^1.4.2
+ prosemirror-view: ^1.33.8
+
+ prosemirror-transform@1.10.4:
+ resolution: {integrity: sha512-pwDy22nAnGqNR1feOQKHxoFkkUtepoFAd3r2hbEDsnf4wp57kKA36hXsB3njA9FtONBEwSDnDeCiJe+ItD+ykw==}
+
+ prosemirror-view@1.41.3:
+ resolution: {integrity: sha512-SqMiYMUQNNBP9kfPhLO8WXEk/fon47vc52FQsUiJzTBuyjKgEcoAwMyF04eQ4WZ2ArMn7+ReypYL60aKngbACQ==}
+
+ proxy-from-env@1.1.0:
+ resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
+
+ punycode.js@2.3.1:
+ resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==}
+ engines: {node: '>=6'}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ qs@6.14.0:
+ resolution: {integrity: sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==}
+ engines: {node: '>=0.6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-datepicker@8.7.0:
+ resolution: {integrity: sha512-r5OJbiLWc3YiVNy69Kau07/aVgVGsFVMA6+nlqCV7vyQ8q0FUOnJ+wAI4CgVxHejG3i5djAEiebrF8/Eip4rIw==}
+ peerDependencies:
+ react: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc
+ react-dom: ^16.9.0 || ^17 || ^18 || ^19 || ^19.0.0-rc
+
+ react-dom@19.1.0:
+ resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==}
+ peerDependencies:
+ react: ^19.1.0
+
+ react-hook-form@7.66.0:
+ resolution: {integrity: sha512-xXBqsWGKrY46ZqaHDo+ZUYiMUgi8suYu5kdrS20EG8KiL7VRQitEbNjm+UcrDYrNi1YLyfpmAeGjCZYXLT9YBw==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ react: ^16.8.0 || ^17 || ^18 || ^19
+
+ react-intersection-observer@10.0.0:
+ resolution: {integrity: sha512-JJRgcnFQoVXmbE5+GXr1OS1NDD1gHk0HyfpLcRf0575IbJz+io8yzs4mWVlfaqOQq1FiVjLvuYAdEEcrrCfveg==}
+ peerDependencies:
+ react: ^17.0.0 || ^18.0.0 || ^19.0.0
+ react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react@19.1.0:
+ resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==}
+ engines: {node: '>=0.10.0'}
+
+ readdirp@4.1.2:
+ resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==}
+ engines: {node: '>= 14.18.0'}
+
+ reflect.getprototypeof@1.0.10:
+ resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
+ engines: {node: '>= 0.4'}
+
+ regenerate-unicode-properties@10.2.2:
+ resolution: {integrity: sha512-m03P+zhBeQd1RGnYxrGyDAPpWX/epKirLrp8e3qevZdVkKtnCrjjWczIbYc8+xd6vcTStVlqfycTx1KR4LOr0g==}
+ engines: {node: '>=4'}
+
+ regenerate@1.4.2:
+ resolution: {integrity: sha512-zrceR/XhGYU/d/opr2EKO7aRHUeiBI8qjtfHqADTwZd6Szfy16la6kqD0MIUs5z5hx6AaKa+PixpPrR289+I0A==}
+
+ regexp.prototype.flags@1.5.4:
+ resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
+ engines: {node: '>= 0.4'}
+
+ regexpu-core@6.4.0:
+ resolution: {integrity: sha512-0ghuzq67LI9bLXpOX/ISfve/Mq33a4aFRzoQYhnnok1JOFpmE/A2TBGkNVenOGEeSBCjIiWcc6MVOG5HEQv0sA==}
+ engines: {node: '>=4'}
+
+ regjsgen@0.8.0:
+ resolution: {integrity: sha512-RvwtGe3d7LvWiDQXeQw8p5asZUmfU1G/l6WbUXeHta7Y2PEIvBTwH6E2EfmYUK8pxcxEdEmaomqyp0vZZ7C+3Q==}
+
+ regjsparser@0.13.0:
+ resolution: {integrity: sha512-NZQZdC5wOE/H3UT28fVGL+ikOZcEzfMGk/c3iN9UGxzWHMa1op7274oyiUVrAG4B2EuFhus8SvkaYnhvW92p9Q==}
+ hasBin: true
+
+ require-from-string@2.0.2:
+ resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
+ engines: {node: '>=0.10.0'}
+
+ 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.10:
+ resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==}
+ 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'}
+
+ rope-sequence@1.3.4:
+ resolution: {integrity: sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==}
+
+ rrweb-cssom@0.8.0:
+ resolution: {integrity: sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==}
+
+ 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-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'}
+
+ safer-buffer@2.1.2:
+ resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==}
+
+ sass@1.93.2:
+ resolution: {integrity: sha512-t+YPtOQHpGW1QWsh1CHQ5cPIr9lbbGZLZnbihP/D/qZj/yuV68m8qarcV17nvkOX81BCrvzAlq2klCQFZghyTg==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ saxes@6.0.0:
+ resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==}
+ engines: {node: '>=v12.22.7'}
+
+ 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.2:
+ resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ set-function-name@2.0.2:
+ resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
+ engines: {node: '>= 0.4'}
+
+ set-proto@1.0.0:
+ resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
+ engines: {node: '>= 0.4'}
+
+ sharp@0.34.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'}
+
+ snake-case@3.0.4:
+ resolution: {integrity: sha512-LAOh4z89bGQvl9pFfNF8V146i7o7/CqFPbqzYgP+yYzDIDeS9HaNFtXABamRW+AQzEVODcvE79ljJ+8a9YSdMg==}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ stable-hash@0.0.5:
+ resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
+
+ stop-iteration-iterator@1.1.0:
+ resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.includes@2.0.1:
+ resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.matchall@4.0.12:
+ resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.repeat@1.0.0:
+ resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
+
+ string.prototype.trim@1.2.10:
+ resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimend@1.0.9:
+ resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimstart@1.0.8:
+ resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ engines: {node: '>= 0.4'}
+
+ strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ react: '>= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0'
+ peerDependenciesMeta:
+ '@babel/core':
+ optional: true
+ babel-plugin-macros:
+ optional: true
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ svg-parser@2.0.4:
+ resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==}
+
+ svgo@3.3.2:
+ resolution: {integrity: sha512-OoohrmuUlBs8B8o6MB2Aevn+pRIH9zDALSR+6hhqVfa6fRwG/Qw9VUMSMW9VNg2CFc/MTIfabtdOVl9ODIJjpw==}
+ engines: {node: '>=14.0.0'}
+ hasBin: true
+
+ swiper@12.0.3:
+ resolution: {integrity: sha512-BHd6U1VPEIksrXlyXjMmRWO0onmdNPaTAFduzqR3pgjvi7KfmUCAm/0cj49u2D7B0zNjMw02TSeXfinC1hDCXg==}
+ engines: {node: '>= 4.7.0'}
+
+ symbol-tree@3.2.4:
+ resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==}
+
+ tabbable@6.2.0:
+ resolution: {integrity: sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==}
+
+ tailwind-merge@3.3.1:
+ resolution: {integrity: sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==}
+
+ tailwindcss@4.1.13:
+ resolution: {integrity: sha512-i+zidfmTqtwquj4hMEwdjshYYgMbOrPzb9a0M3ZgNa0JMoZeFC6bxZvO8yr8ozS6ix2SDz0+mvryPeBs2TFE+w==}
+
+ tapable@2.2.3:
+ resolution: {integrity: sha512-ZL6DDuAlRlLGghwcfmSn9sK3Hr6ArtyudlSAiCqQ6IfE+b+HHbydbYDIG15IfS5do+7XQQBdBiubF/cV2dnDzg==}
+ engines: {node: '>=6'}
+
+ tar@7.5.1:
+ resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.15:
+ resolution: {integrity: sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==}
+ engines: {node: '>=12.0.0'}
+
+ tldts-core@7.0.17:
+ resolution: {integrity: sha512-DieYoGrP78PWKsrXr8MZwtQ7GLCUeLxihtjC1jZsW1DnvSMdKPitJSe8OSYDM2u5H6g3kWJZpePqkp43TfLh0g==}
+
+ tldts@7.0.17:
+ resolution: {integrity: sha512-Y1KQBgDd/NUc+LfOtKS6mNsC9CCaH+m2P1RoIZy7RAPo3C3/t8X45+zgut31cRZtZ3xKPjfn3TkGTrctC2TQIQ==}
+ hasBin: true
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ tough-cookie@6.0.0:
+ resolution: {integrity: sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==}
+ engines: {node: '>=16'}
+
+ tr46@6.0.0:
+ resolution: {integrity: sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==}
+ engines: {node: '>=20'}
+
+ 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==}
+
+ 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.2:
+ resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ uc.micro@2.1.0:
+ resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==}
+
+ 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==}
+
+ unicode-canonical-property-names-ecmascript@2.0.1:
+ resolution: {integrity: sha512-dA8WbNeb2a6oQzAQ55YlT5vQAWGV9WXOsi3SskE3bcCdM0P4SDd+24zS/OCacdRq5BkdsRj9q3Pg6YyQoxIGqg==}
+ engines: {node: '>=4'}
+
+ unicode-match-property-ecmascript@2.0.0:
+ resolution: {integrity: sha512-5kaZCrbp5mmbz5ulBkDkbY0SsPOjKqVS35VpL9ulMPfSl0J0Xsm+9Evphv9CoIZFwre7aJoa94AY6seMKGVN5Q==}
+ engines: {node: '>=4'}
+
+ unicode-match-property-value-ecmascript@2.2.1:
+ resolution: {integrity: sha512-JQ84qTuMg4nVkx8ga4A16a1epI9H6uTXAknqxkGF/aFfRLw1xC/Bp24HNLaZhHSkWd3+84t8iXnp1J0kYcZHhg==}
+ engines: {node: '>=4'}
+
+ unicode-property-aliases-ecmascript@2.2.0:
+ resolution: {integrity: sha512-hpbDzxUY9BFwX+UeBnxv3Sh1q7HFxj48DTmXchNgRa46lO8uj3/1iEn3MiNUYTg1g9ctIqXCCERn8gYZhHC5lQ==}
+ engines: {node: '>=4'}
+
+ unrs-resolver@1.11.1:
+ resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==}
+
+ update-browserslist-db@1.1.3:
+ resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ 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==}
+
+ w3c-keyname@2.2.8:
+ resolution: {integrity: sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==}
+
+ w3c-xmlserializer@5.0.0:
+ resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
+ engines: {node: '>=18'}
+
+ webidl-conversions@8.0.0:
+ resolution: {integrity: sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==}
+ engines: {node: '>=20'}
+
+ whatwg-encoding@3.1.1:
+ resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
+ engines: {node: '>=18'}
+
+ whatwg-mimetype@4.0.0:
+ resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==}
+ engines: {node: '>=18'}
+
+ whatwg-url@15.1.0:
+ resolution: {integrity: sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==}
+ engines: {node: '>=20'}
+
+ 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
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrappy@1.0.2:
+ resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==}
+
+ ws@8.18.3:
+ resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==}
+ engines: {node: '>=10.0.0'}
+ peerDependencies:
+ bufferutil: ^4.0.1
+ utf-8-validate: '>=5.0.2'
+ peerDependenciesMeta:
+ bufferutil:
+ optional: true
+ utf-8-validate:
+ optional: true
+
+ xml-name-validator@5.0.0:
+ resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==}
+ engines: {node: '>=18'}
+
+ xmlchars@2.2.0:
+ resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yallist@5.0.0:
+ resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+ engines: {node: '>=18'}
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zod@4.1.12:
+ resolution: {integrity: sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ==}
+
+ 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': {}
+
+ '@asamuzakjp/css-color@4.0.5':
+ dependencies:
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-color-parser': 3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+ lru-cache: 11.2.2
+
+ '@asamuzakjp/dom-selector@6.7.3':
+ dependencies:
+ '@asamuzakjp/nwsapi': 2.3.9
+ bidi-js: 1.0.3
+ css-tree: 3.1.0
+ is-potential-custom-element-name: 1.0.1
+ lru-cache: 11.2.2
+
+ '@asamuzakjp/nwsapi@2.3.9': {}
+
+ '@babel/code-frame@7.27.1':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.27.1
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.28.4': {}
+
+ '@babel/core@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helpers': 7.28.4
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.28.3':
+ dependencies:
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-annotate-as-pure@7.27.3':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-compilation-targets@7.27.2':
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/helper-validator-option': 7.27.1
+ browserslist: 4.26.3
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-create-class-features-plugin@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/traverse': 7.28.4
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-create-regexp-features-plugin@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ regexpu-core: 6.4.0
+ semver: 6.3.1
+
+ '@babel/helper-define-polyfill-provider@0.6.5(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-plugin-utils': 7.27.1
+ debug: 4.4.3
+ lodash.debounce: 4.0.8
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-globals@7.28.0': {}
+
+ '@babel/helper-member-expression-to-functions@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-imports@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-optimise-call-expression@7.27.1':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/helper-plugin-utils@7.27.1': {}
+
+ '@babel/helper-remap-async-to-generator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-wrap-function': 7.28.3
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-replace-supers@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-member-expression-to-functions': 7.27.1
+ '@babel/helper-optimise-call-expression': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-skip-transparent-expression-wrappers@7.27.1':
+ dependencies:
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.27.1': {}
+
+ '@babel/helper-validator-identifier@7.27.1': {}
+
+ '@babel/helper-validator-option@7.27.1': {}
+
+ '@babel/helper-wrap-function@7.28.3':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/traverse': 7.28.4
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helpers@7.28.4':
+ dependencies:
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+
+ '@babel/parser@7.28.4':
+ dependencies:
+ '@babel/types': 7.28.4
+
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-proposal-private-property-in-object@7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@babel/plugin-syntax-import-assertions@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-import-attributes@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-jsx@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-typescript@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-syntax-unicode-sets-regex@7.18.6(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-arrow-functions@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-async-generator-functions@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4)
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-async-to-generator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-block-scoped-functions@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-block-scoping@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-class-properties@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-class-static-block@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-classes@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-globals': 7.28.0
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-computed-properties@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/template': 7.27.2
+
+ '@babel/plugin-transform-destructuring@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-dotall-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-duplicate-keys@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-dynamic-import@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-explicit-resource-management@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-exponentiation-operator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-export-namespace-from@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-for-of@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-function-name@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-json-strings@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-literals@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-logical-assignment-operators@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-member-expression-literals@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-named-capturing-groups-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-new-target@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-nullish-coalescing-operator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-numeric-separator@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-object-rest-spread@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4)
+ '@babel/traverse': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-object-super@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-optional-catch-binding@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-optional-chaining@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-parameters@7.27.7(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-private-methods@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-private-property-in-object@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-property-literals@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-react-constant-elements@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-react-display-name@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-react-jsx-development@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-react-jsx@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-module-imports': 7.27.1
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/types': 7.28.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-react-pure-annotations@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-regenerator@7.28.4(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-regexp-modifiers@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-reserved-words@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-shorthand-properties@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-spread@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-sticky-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-template-literals@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typeof-symbol@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-typescript@7.28.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-annotate-as-pure': 7.27.3
+ '@babel/helper-create-class-features-plugin': 7.28.3(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-skip-transparent-expression-wrappers': 7.27.1
+ '@babel/plugin-syntax-typescript': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/plugin-transform-unicode-escapes@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-unicode-property-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-unicode-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/plugin-transform-unicode-sets-regex@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-create-regexp-features-plugin': 7.27.1(@babel/core@7.28.4)
+ '@babel/helper-plugin-utils': 7.27.1
+
+ '@babel/preset-env@7.28.3(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/core': 7.28.4
+ '@babel/helper-compilation-targets': 7.27.2
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-bugfix-firefox-class-in-computed-class-key': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-bugfix-safari-class-field-initializer-scope': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-bugfix-v8-static-class-fields-redefine-readonly': 7.28.3(@babel/core@7.28.4)
+ '@babel/plugin-proposal-private-property-in-object': 7.21.0-placeholder-for-preset-env.2(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-assertions': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-import-attributes': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-syntax-unicode-sets-regex': 7.18.6(@babel/core@7.28.4)
+ '@babel/plugin-transform-arrow-functions': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-async-generator-functions': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-async-to-generator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-block-scoped-functions': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-block-scoping': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-class-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-class-static-block': 7.28.3(@babel/core@7.28.4)
+ '@babel/plugin-transform-classes': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-computed-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-dotall-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-duplicate-keys': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-duplicate-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-dynamic-import': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-explicit-resource-management': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-exponentiation-operator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-export-namespace-from': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-for-of': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-function-name': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-json-strings': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-literals': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-logical-assignment-operators': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-member-expression-literals': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-amd': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-systemjs': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-umd': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-named-capturing-groups-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-new-target': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-nullish-coalescing-operator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-numeric-separator': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-object-rest-spread': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-object-super': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-optional-catch-binding': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-optional-chaining': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.4)
+ '@babel/plugin-transform-private-methods': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-private-property-in-object': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-property-literals': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-regenerator': 7.28.4(@babel/core@7.28.4)
+ '@babel/plugin-transform-regexp-modifiers': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-reserved-words': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-shorthand-properties': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-spread': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-sticky-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-template-literals': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-typeof-symbol': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-unicode-escapes': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-unicode-property-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-unicode-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-unicode-sets-regex': 7.27.1(@babel/core@7.28.4)
+ '@babel/preset-modules': 0.1.6-no-external-plugins(@babel/core@7.28.4)
+ babel-plugin-polyfill-corejs2: 0.4.14(@babel/core@7.28.4)
+ babel-plugin-polyfill-corejs3: 0.13.0(@babel/core@7.28.4)
+ babel-plugin-polyfill-regenerator: 0.6.5(@babel/core@7.28.4)
+ core-js-compat: 3.45.1
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/preset-modules@0.1.6-no-external-plugins(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/types': 7.28.4
+ esutils: 2.0.3
+
+ '@babel/preset-react@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-transform-react-display-name': 7.28.0(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-jsx-development': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-react-pure-annotations': 7.27.1(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/preset-typescript@7.27.1(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-plugin-utils': 7.27.1
+ '@babel/helper-validator-option': 7.27.1
+ '@babel/plugin-syntax-jsx': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-modules-commonjs': 7.27.1(@babel/core@7.28.4)
+ '@babel/plugin-transform-typescript': 7.28.0(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/template@7.27.2':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/parser': 7.28.4
+ '@babel/types': 7.28.4
+
+ '@babel/traverse@7.28.4':
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ '@babel/generator': 7.28.3
+ '@babel/helper-globals': 7.28.0
+ '@babel/parser': 7.28.4
+ '@babel/template': 7.27.2
+ '@babel/types': 7.28.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.28.4':
+ dependencies:
+ '@babel/helper-string-parser': 7.27.1
+ '@babel/helper-validator-identifier': 7.27.1
+
+ '@csstools/color-helpers@5.1.0': {}
+
+ '@csstools/css-calc@2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-color-parser@3.1.0(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/color-helpers': 5.1.0
+ '@csstools/css-calc': 2.1.4(@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4))(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-parser-algorithms': 3.0.5(@csstools/css-tokenizer@3.0.4)
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-parser-algorithms@3.0.5(@csstools/css-tokenizer@3.0.4)':
+ dependencies:
+ '@csstools/css-tokenizer': 3.0.4
+
+ '@csstools/css-syntax-patches-for-csstree@1.0.14(postcss@8.5.6)':
+ dependencies:
+ postcss: 8.5.6
+
+ '@csstools/css-tokenizer@3.0.4': {}
+
+ '@emnapi/core@1.5.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.1.0
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.5.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.1.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@eslint-community/eslint-utils@4.9.0(eslint@9.36.0(jiti@2.6.0))':
+ dependencies:
+ eslint: 9.36.0(jiti@2.6.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.1': {}
+
+ '@eslint/config-array@0.21.0':
+ dependencies:
+ '@eslint/object-schema': 2.1.6
+ debug: 4.4.3
+ minimatch: 3.1.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.3.1': {}
+
+ '@eslint/core@0.15.2':
+ 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.36.0': {}
+
+ '@eslint/object-schema@2.1.6': {}
+
+ '@eslint/plugin-kit@0.3.5':
+ dependencies:
+ '@eslint/core': 0.15.2
+ 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.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@floating-ui/dom': 1.7.4
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ '@floating-ui/react@0.27.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@floating-ui/react-dom': 2.1.6(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ '@floating-ui/utils': 0.2.10
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ tabbable: 6.2.0
+
+ '@floating-ui/utils@0.2.10': {}
+
+ '@hookform/resolvers@5.2.2(react-hook-form@7.66.0(react@19.1.0))':
+ dependencies:
+ '@standard-schema/utils': 0.3.0
+ react-hook-form: 7.66.0(react@19.1.0)
+
+ '@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.5.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
+
+ '@isaacs/fs-minipass@4.0.1':
+ dependencies:
+ minipass: 7.1.2
+
+ '@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.5.0
+ '@emnapi/runtime': 1.5.0
+ '@tybys/wasm-util': 0.10.1
+ optional: true
+
+ '@next/env@16.0.10': {}
+
+ '@next/eslint-plugin-next@15.5.4':
+ dependencies:
+ fast-glob: 3.3.1
+
+ '@next/swc-darwin-arm64@16.0.10':
+ optional: true
+
+ '@next/swc-darwin-x64@16.0.10':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@16.0.10':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@16.0.10':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@16.0.10':
+ optional: true
+
+ '@next/swc-linux-x64-musl@16.0.10':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@16.0.10':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@16.0.10':
+ 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': {}
+
+ '@parcel/watcher-android-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.1':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.1':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.1':
+ optional: true
+
+ '@parcel/watcher@2.5.1':
+ dependencies:
+ detect-libc: 1.0.3
+ is-glob: 4.0.3
+ micromatch: 4.0.8
+ node-addon-api: 7.1.1
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.1
+ '@parcel/watcher-darwin-arm64': 2.5.1
+ '@parcel/watcher-darwin-x64': 2.5.1
+ '@parcel/watcher-freebsd-x64': 2.5.1
+ '@parcel/watcher-linux-arm-glibc': 2.5.1
+ '@parcel/watcher-linux-arm-musl': 2.5.1
+ '@parcel/watcher-linux-arm64-glibc': 2.5.1
+ '@parcel/watcher-linux-arm64-musl': 2.5.1
+ '@parcel/watcher-linux-x64-glibc': 2.5.1
+ '@parcel/watcher-linux-x64-musl': 2.5.1
+ '@parcel/watcher-win32-arm64': 2.5.1
+ '@parcel/watcher-win32-ia32': 2.5.1
+ '@parcel/watcher-win32-x64': 2.5.1
+ optional: true
+
+ '@remirror/core-constants@3.0.0': {}
+
+ '@rtsao/scc@1.1.0': {}
+
+ '@rushstack/eslint-patch@1.12.0': {}
+
+ '@standard-schema/utils@0.3.0': {}
+
+ '@svgr/babel-plugin-add-jsx-attribute@8.0.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-plugin-replace-jsx-attribute-value@8.0.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-plugin-svg-dynamic-title@8.0.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-plugin-svg-em-dimensions@8.0.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-plugin-transform-react-native-svg@8.1.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-plugin-transform-svg-component@8.0.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+
+ '@svgr/babel-preset@8.1.0(@babel/core@7.28.4)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@svgr/babel-plugin-add-jsx-attribute': 8.0.0(@babel/core@7.28.4)
+ '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.28.4)
+ '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.28.4)
+ '@svgr/babel-plugin-replace-jsx-attribute-value': 8.0.0(@babel/core@7.28.4)
+ '@svgr/babel-plugin-svg-dynamic-title': 8.0.0(@babel/core@7.28.4)
+ '@svgr/babel-plugin-svg-em-dimensions': 8.0.0(@babel/core@7.28.4)
+ '@svgr/babel-plugin-transform-react-native-svg': 8.1.0(@babel/core@7.28.4)
+ '@svgr/babel-plugin-transform-svg-component': 8.0.0(@babel/core@7.28.4)
+
+ '@svgr/cli@8.1.0(typescript@5.9.2)':
+ dependencies:
+ '@svgr/core': 8.1.0(typescript@5.9.2)
+ '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))
+ '@svgr/plugin-prettier': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))
+ '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)
+ camelcase: 6.3.0
+ chalk: 4.1.2
+ commander: 9.5.0
+ dashify: 2.0.0
+ glob: 8.1.0
+ snake-case: 3.0.4
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@svgr/core@8.1.0(typescript@5.9.2)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4)
+ camelcase: 6.3.0
+ cosmiconfig: 8.3.6(typescript@5.9.2)
+ snake-case: 3.0.4
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@svgr/hast-util-to-babel-ast@8.0.0':
+ dependencies:
+ '@babel/types': 7.28.4
+ entities: 4.5.0
+
+ '@svgr/plugin-jsx@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@svgr/babel-preset': 8.1.0(@babel/core@7.28.4)
+ '@svgr/core': 8.1.0(typescript@5.9.2)
+ '@svgr/hast-util-to-babel-ast': 8.0.0
+ svg-parser: 2.0.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@svgr/plugin-prettier@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))':
+ dependencies:
+ '@svgr/core': 8.1.0(typescript@5.9.2)
+ deepmerge: 4.3.1
+ prettier: 2.8.8
+
+ '@svgr/plugin-svgo@8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)':
+ dependencies:
+ '@svgr/core': 8.1.0(typescript@5.9.2)
+ cosmiconfig: 8.3.6(typescript@5.9.2)
+ deepmerge: 4.3.1
+ svgo: 3.3.2
+ transitivePeerDependencies:
+ - typescript
+
+ '@svgr/webpack@8.1.0(typescript@5.9.2)':
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/plugin-transform-react-constant-elements': 7.27.1(@babel/core@7.28.4)
+ '@babel/preset-env': 7.28.3(@babel/core@7.28.4)
+ '@babel/preset-react': 7.27.1(@babel/core@7.28.4)
+ '@babel/preset-typescript': 7.27.1(@babel/core@7.28.4)
+ '@svgr/core': 8.1.0(typescript@5.9.2)
+ '@svgr/plugin-jsx': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))
+ '@svgr/plugin-svgo': 8.1.0(@svgr/core@8.1.0(typescript@5.9.2))(typescript@5.9.2)
+ transitivePeerDependencies:
+ - supports-color
+ - typescript
+
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
+ '@tailwindcss/node@4.1.13':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.18.3
+ jiti: 2.6.0
+ lightningcss: 1.30.1
+ magic-string: 0.30.19
+ source-map-js: 1.2.1
+ tailwindcss: 4.1.13
+
+ '@tailwindcss/oxide-android-arm64@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.1.13':
+ optional: true
+
+ '@tailwindcss/oxide@4.1.13':
+ dependencies:
+ detect-libc: 2.1.1
+ tar: 7.5.1
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.1.13
+ '@tailwindcss/oxide-darwin-arm64': 4.1.13
+ '@tailwindcss/oxide-darwin-x64': 4.1.13
+ '@tailwindcss/oxide-freebsd-x64': 4.1.13
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.13
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.1.13
+ '@tailwindcss/oxide-linux-arm64-musl': 4.1.13
+ '@tailwindcss/oxide-linux-x64-gnu': 4.1.13
+ '@tailwindcss/oxide-linux-x64-musl': 4.1.13
+ '@tailwindcss/oxide-wasm32-wasi': 4.1.13
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.1.13
+ '@tailwindcss/oxide-win32-x64-msvc': 4.1.13
+
+ '@tailwindcss/postcss@4.1.13':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.1.13
+ '@tailwindcss/oxide': 4.1.13
+ postcss: 8.5.6
+ tailwindcss: 4.1.13
+
+ '@tailwindcss/typography@0.5.19(tailwindcss@4.1.13)':
+ dependencies:
+ postcss-selector-parser: 6.0.10
+ tailwindcss: 4.1.13
+
+ '@tanstack/query-core@5.90.5': {}
+
+ '@tanstack/react-query@5.90.5(react@19.1.0)':
+ dependencies:
+ '@tanstack/query-core': 5.90.5
+ react: 19.1.0
+
+ '@tiptap/core@3.7.2(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@tiptap/pm': 3.7.2
+
+ '@tiptap/extension-blockquote@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-bold@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-bubble-menu@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@floating-ui/dom': 1.7.4
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+ optional: true
+
+ '@tiptap/extension-bullet-list@3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/extension-list': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-code-block@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+
+ '@tiptap/extension-code@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-document@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-dropcursor@3.7.2(@tiptap/extensions@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/extensions': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-floating-menu@3.7.2(@floating-ui/dom@1.7.4)(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@floating-ui/dom': 1.7.4
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+ optional: true
+
+ '@tiptap/extension-gapcursor@3.7.2(@tiptap/extensions@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/extensions': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-hard-break@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-heading@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-horizontal-rule@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+
+ '@tiptap/extension-italic@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-link@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+ linkifyjs: 4.3.2
+
+ '@tiptap/extension-list-item@3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/extension-list': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-list-keymap@3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/extension-list': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+
+ '@tiptap/extension-ordered-list@3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/extension-list': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-paragraph@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-placeholder@3.7.2(@tiptap/extensions@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/extensions': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-strike@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-text@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extension-underline@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+
+ '@tiptap/extensions@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+
+ '@tiptap/pm@3.7.2':
+ dependencies:
+ prosemirror-changeset: 2.3.1
+ prosemirror-collab: 1.3.1
+ prosemirror-commands: 1.7.1
+ prosemirror-dropcursor: 1.8.2
+ prosemirror-gapcursor: 1.4.0
+ prosemirror-history: 1.4.1
+ prosemirror-inputrules: 1.5.1
+ prosemirror-keymap: 1.2.3
+ prosemirror-markdown: 1.13.2
+ prosemirror-menu: 1.2.5
+ prosemirror-model: 1.25.3
+ prosemirror-schema-basic: 1.2.4
+ prosemirror-schema-list: 1.5.1
+ prosemirror-state: 1.4.3
+ prosemirror-tables: 1.8.1
+ prosemirror-trailing-node: 3.0.0(prosemirror-model@1.25.3)(prosemirror-state@1.4.3)(prosemirror-view@1.41.3)
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.41.3
+
+ '@tiptap/react@3.7.2(@floating-ui/dom@1.7.4)(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)(@types/react-dom@19.1.9(@types/react@19.1.16))(@types/react@19.1.16)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+ '@types/react': 19.1.16
+ '@types/react-dom': 19.1.9(@types/react@19.1.16)
+ '@types/use-sync-external-store': 0.0.6
+ fast-deep-equal: 3.1.3
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ use-sync-external-store: 1.6.0(react@19.1.0)
+ optionalDependencies:
+ '@tiptap/extension-bubble-menu': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/extension-floating-menu': 3.7.2(@floating-ui/dom@1.7.4)(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ transitivePeerDependencies:
+ - '@floating-ui/dom'
+
+ '@tiptap/starter-kit@3.7.2':
+ dependencies:
+ '@tiptap/core': 3.7.2(@tiptap/pm@3.7.2)
+ '@tiptap/extension-blockquote': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-bold': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-bullet-list': 3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))
+ '@tiptap/extension-code': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-code-block': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/extension-document': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-dropcursor': 3.7.2(@tiptap/extensions@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))
+ '@tiptap/extension-gapcursor': 3.7.2(@tiptap/extensions@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))
+ '@tiptap/extension-hard-break': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-heading': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-horizontal-rule': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/extension-italic': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-link': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/extension-list': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/extension-list-item': 3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))
+ '@tiptap/extension-list-keymap': 3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))
+ '@tiptap/extension-ordered-list': 3.7.2(@tiptap/extension-list@3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2))
+ '@tiptap/extension-paragraph': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-strike': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-text': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extension-underline': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))
+ '@tiptap/extensions': 3.7.2(@tiptap/core@3.7.2(@tiptap/pm@3.7.2))(@tiptap/pm@3.7.2)
+ '@tiptap/pm': 3.7.2
+
+ '@trysound/sax@0.2.0': {}
+
+ '@tybys/wasm-util@0.10.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/axios@0.14.4':
+ dependencies:
+ axios: 1.12.2
+ transitivePeerDependencies:
+ - debug
+
+ '@types/estree@1.0.8': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/json5@0.0.29': {}
+
+ '@types/linkify-it@5.0.0': {}
+
+ '@types/markdown-it@14.1.2':
+ dependencies:
+ '@types/linkify-it': 5.0.0
+ '@types/mdurl': 2.0.0
+
+ '@types/mdurl@2.0.0': {}
+
+ '@types/node@20.19.18':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/qs@6.14.0': {}
+
+ '@types/react-dom@19.1.9(@types/react@19.1.16)':
+ dependencies:
+ '@types/react': 19.1.16
+
+ '@types/react@19.1.16':
+ dependencies:
+ csstype: 3.1.3
+
+ '@types/trusted-types@2.0.7':
+ optional: true
+
+ '@types/use-sync-external-store@0.0.6': {}
+
+ '@typescript-eslint/eslint-plugin@8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.1
+ '@typescript-eslint/parser': 8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ '@typescript-eslint/scope-manager': 8.45.0
+ '@typescript-eslint/type-utils': 8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.45.0
+ eslint: 9.36.0(jiti@2.6.0)
+ graphemer: 1.4.0
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.45.0
+ '@typescript-eslint/types': 8.45.0
+ '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.2)
+ '@typescript-eslint/visitor-keys': 8.45.0
+ debug: 4.4.3
+ eslint: 9.36.0(jiti@2.6.0)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.45.0(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.45.0
+ debug: 4.4.3
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.45.0':
+ dependencies:
+ '@typescript-eslint/types': 8.45.0
+ '@typescript-eslint/visitor-keys': 8.45.0
+
+ '@typescript-eslint/tsconfig-utils@8.45.0(typescript@5.9.2)':
+ dependencies:
+ typescript: 5.9.2
+
+ '@typescript-eslint/type-utils@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/types': 8.45.0
+ '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.2)
+ '@typescript-eslint/utils': 8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ debug: 4.4.3
+ eslint: 9.36.0(jiti@2.6.0)
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.45.0': {}
+
+ '@typescript-eslint/typescript-estree@8.45.0(typescript@5.9.2)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.45.0(typescript@5.9.2)
+ '@typescript-eslint/tsconfig-utils': 8.45.0(typescript@5.9.2)
+ '@typescript-eslint/types': 8.45.0
+ '@typescript-eslint/visitor-keys': 8.45.0
+ debug: 4.4.3
+ fast-glob: 3.3.3
+ is-glob: 4.0.3
+ minimatch: 9.0.5
+ semver: 7.7.2
+ ts-api-utils: 2.1.0(typescript@5.9.2)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0))
+ '@typescript-eslint/scope-manager': 8.45.0
+ '@typescript-eslint/types': 8.45.0
+ '@typescript-eslint/typescript-estree': 8.45.0(typescript@5.9.2)
+ eslint: 9.36.0(jiti@2.6.0)
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.45.0':
+ dependencies:
+ '@typescript-eslint/types': 8.45.0
+ 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
+
+ acorn-jsx@5.3.2(acorn@8.15.0):
+ dependencies:
+ acorn: 8.15.0
+
+ acorn@8.15.0: {}
+
+ agent-base@7.1.4: {}
+
+ 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-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.1
+ 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.1
+ is-array-buffer: 3.0.5
+
+ ast-types-flow@0.0.8: {}
+
+ async-function@1.0.0: {}
+
+ async-generator-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.10.3: {}
+
+ 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: {}
+
+ babel-plugin-polyfill-corejs2@0.4.14(@babel/core@7.28.4):
+ dependencies:
+ '@babel/compat-data': 7.28.4
+ '@babel/core': 7.28.4
+ '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4)
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-polyfill-corejs3@0.13.0(@babel/core@7.28.4):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4)
+ core-js-compat: 3.45.1
+ transitivePeerDependencies:
+ - supports-color
+
+ babel-plugin-polyfill-regenerator@0.6.5(@babel/core@7.28.4):
+ dependencies:
+ '@babel/core': 7.28.4
+ '@babel/helper-define-polyfill-provider': 0.6.5(@babel/core@7.28.4)
+ transitivePeerDependencies:
+ - supports-color
+
+ balanced-match@1.0.2: {}
+
+ baseline-browser-mapping@2.8.13: {}
+
+ bidi-js@1.0.3:
+ dependencies:
+ require-from-string: 2.0.2
+
+ boolbase@1.0.0: {}
+
+ 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
+
+ browserslist@4.26.3:
+ dependencies:
+ baseline-browser-mapping: 2.8.13
+ caniuse-lite: 1.0.30001746
+ electron-to-chromium: 1.5.233
+ node-releases: 2.0.23
+ update-browserslist-db: 1.1.3(browserslist@4.26.3)
+
+ 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.1
+ set-function-length: 1.2.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.1
+
+ callsites@3.1.0: {}
+
+ camelcase@6.3.0: {}
+
+ caniuse-lite@1.0.30001746: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ chokidar@4.0.3:
+ dependencies:
+ readdirp: 4.1.2
+
+ chownr@3.0.0: {}
+
+ 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
+
+ commander@7.2.0: {}
+
+ commander@9.5.0: {}
+
+ concat-map@0.0.1: {}
+
+ convert-source-map@2.0.0: {}
+
+ core-js-compat@3.45.1:
+ dependencies:
+ browserslist: 4.26.3
+
+ cosmiconfig@8.3.6(typescript@5.9.2):
+ dependencies:
+ import-fresh: 3.3.1
+ js-yaml: 4.1.0
+ parse-json: 5.2.0
+ path-type: 4.0.0
+ optionalDependencies:
+ typescript: 5.9.2
+
+ crelt@1.0.6: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ css-select@5.2.2:
+ dependencies:
+ boolbase: 1.0.0
+ css-what: 6.2.2
+ domhandler: 5.0.3
+ domutils: 3.2.2
+ nth-check: 2.1.1
+
+ css-tree@2.2.1:
+ dependencies:
+ mdn-data: 2.0.28
+ source-map-js: 1.2.1
+
+ css-tree@2.3.1:
+ dependencies:
+ mdn-data: 2.0.30
+ source-map-js: 1.2.1
+
+ css-tree@3.1.0:
+ dependencies:
+ mdn-data: 2.12.2
+ source-map-js: 1.2.1
+
+ css-what@6.2.2: {}
+
+ cssesc@3.0.0: {}
+
+ csso@5.0.5:
+ dependencies:
+ css-tree: 2.2.1
+
+ cssstyle@5.3.1(postcss@8.5.6):
+ dependencies:
+ '@asamuzakjp/css-color': 4.0.5
+ '@csstools/css-syntax-patches-for-csstree': 1.0.14(postcss@8.5.6)
+ css-tree: 3.1.0
+ transitivePeerDependencies:
+ - postcss
+
+ csstype@3.1.3: {}
+
+ damerau-levenshtein@1.0.8: {}
+
+ dashify@2.0.0: {}
+
+ data-urls@6.0.0:
+ dependencies:
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 15.1.0
+
+ 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@4.1.0: {}
+
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ decimal.js@10.6.0: {}
+
+ deep-is@0.1.4: {}
+
+ deepmerge@4.3.1: {}
+
+ 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@1.0.3:
+ optional: true
+
+ detect-libc@2.1.1: {}
+
+ doctrine@2.1.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dom-serializer@2.0.0:
+ dependencies:
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+ entities: 4.5.0
+
+ domelementtype@2.3.0: {}
+
+ domhandler@5.0.3:
+ dependencies:
+ domelementtype: 2.3.0
+
+ dompurify@3.3.0:
+ optionalDependencies:
+ '@types/trusted-types': 2.0.7
+
+ domutils@3.2.2:
+ dependencies:
+ dom-serializer: 2.0.0
+ domelementtype: 2.3.0
+ domhandler: 5.0.3
+
+ dot-case@3.0.4:
+ dependencies:
+ no-case: 3.0.4
+ tslib: 2.8.1
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ electron-to-chromium@1.5.233: {}
+
+ emoji-regex@9.2.2: {}
+
+ enhanced-resolve@5.18.3:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.2.3
+
+ entities@4.5.0: {}
+
+ entities@6.0.1: {}
+
+ error-ex@1.3.4:
+ dependencies:
+ is-arrayish: 0.2.1
+
+ 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.1
+ 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.1
+ 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-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.1
+ 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
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-config-next@15.5.4(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2):
+ dependencies:
+ '@next/eslint-plugin-next': 15.5.4
+ '@rushstack/eslint-patch': 1.12.0
+ '@typescript-eslint/eslint-plugin': 8.45.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ '@typescript-eslint/parser': 8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ eslint: 9.36.0(jiti@2.6.0)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.0))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.36.0(jiti@2.6.0))
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@9.36.0(jiti@2.6.0))
+ eslint-plugin-react: 7.37.5(eslint@9.36.0(jiti@2.6.0))
+ eslint-plugin-react-hooks: 5.2.0(eslint@9.36.0(jiti@2.6.0))
+ optionalDependencies:
+ typescript: 5.9.2
+ transitivePeerDependencies:
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+
+ eslint-import-resolver-node@0.3.9:
+ dependencies:
+ debug: 3.2.7
+ is-core-module: 2.16.1
+ resolve: 1.22.10
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ '@nolyfill/is-core-module': 1.0.39
+ debug: 4.4.3
+ eslint: 9.36.0(jiti@2.6.0)
+ get-tsconfig: 4.10.1
+ 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.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.36.0(jiti@2.6.0))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-module-utils@2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ debug: 3.2.7
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ eslint: 9.36.0(jiti@2.6.0)
+ eslint-import-resolver-node: 0.3.9
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.36.0(jiti@2.6.0))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint-import-resolver-typescript@3.10.1)(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.9
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 9.36.0(jiti@2.6.0)
+ eslint-import-resolver-node: 0.3.9
+ eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.10.1)(eslint@9.36.0(jiti@2.6.0))
+ 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.45.0(eslint@9.36.0(jiti@2.6.0))(typescript@5.9.2)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-jsx-a11y@6.10.2(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ aria-query: 5.3.2
+ array-includes: 3.1.9
+ array.prototype.flatmap: 1.3.3
+ ast-types-flow: 0.0.8
+ axe-core: 4.10.3
+ axobject-query: 4.1.0
+ damerau-levenshtein: 1.0.8
+ emoji-regex: 9.2.2
+ eslint: 9.36.0(jiti@2.6.0)
+ 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-react-hooks@5.2.0(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ eslint: 9.36.0(jiti@2.6.0)
+
+ eslint-plugin-react@7.37.5(eslint@9.36.0(jiti@2.6.0)):
+ dependencies:
+ array-includes: 3.1.9
+ array.prototype.findlast: 1.2.5
+ array.prototype.flatmap: 1.3.3
+ array.prototype.tosorted: 1.1.4
+ doctrine: 2.1.0
+ es-iterator-helpers: 1.2.1
+ eslint: 9.36.0(jiti@2.6.0)
+ 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.36.0(jiti@2.6.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.0(eslint@9.36.0(jiti@2.6.0))
+ '@eslint-community/regexpp': 4.12.1
+ '@eslint/config-array': 0.21.0
+ '@eslint/config-helpers': 0.3.1
+ '@eslint/core': 0.15.2
+ '@eslint/eslintrc': 3.3.1
+ '@eslint/js': 9.36.0
+ '@eslint/plugin-kit': 0.3.5
+ '@humanfs/node': 0.16.7
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.8
+ '@types/json-schema': 7.0.15
+ 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.0
+ 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: {}
+
+ esutils@2.0.3: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.1:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-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
+
+ 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.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ motion-dom: 12.23.23
+ motion-utils: 12.23.6
+ tslib: 2.8.1
+ optionalDependencies:
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ fs.realpath@1.0.0: {}
+
+ 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.0: {}
+
+ gensync@1.0.0-beta.2: {}
+
+ get-intrinsic@1.3.1:
+ dependencies:
+ async-function: 1.0.0
+ async-generator-function: 1.0.0
+ 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
+ generator-function: 2.0.0
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.2
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.1
+
+ get-symbol-description@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.1
+
+ get-tsconfig@4.10.1:
+ 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
+
+ glob@8.1.0:
+ dependencies:
+ fs.realpath: 1.0.0
+ inflight: 1.0.6
+ inherits: 2.0.4
+ minimatch: 5.1.6
+ once: 1.4.0
+
+ 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
+
+ html-encoding-sniffer@4.0.0:
+ dependencies:
+ whatwg-encoding: 3.1.1
+
+ http-proxy-agent@7.0.2:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ https-proxy-agent@7.0.6:
+ dependencies:
+ agent-base: 7.1.4
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ iconv-lite@0.6.3:
+ dependencies:
+ safer-buffer: 2.1.2
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ immutable@5.1.4: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ inflight@1.0.6:
+ dependencies:
+ once: 1.4.0
+ wrappy: 1.0.2
+
+ 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.1
+
+ is-arrayish@0.2.1: {}
+
+ 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.2
+
+ 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.1
+ 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.0:
+ dependencies:
+ call-bound: 1.0.4
+ 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-potential-custom-element-name@1.0.1: {}
+
+ 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.1
+
+ isarray@2.0.5: {}
+
+ isexe@2.0.0: {}
+
+ isomorphic-dompurify@2.30.1(postcss@8.5.6):
+ dependencies:
+ dompurify: 3.3.0
+ jsdom: 27.0.1(postcss@8.5.6)
+ transitivePeerDependencies:
+ - bufferutil
+ - canvas
+ - postcss
+ - supports-color
+ - utf-8-validate
+
+ iterator.prototype@1.1.5:
+ dependencies:
+ define-data-property: 1.1.4
+ es-object-atoms: 1.1.1
+ get-intrinsic: 1.3.1
+ get-proto: 1.0.1
+ has-symbols: 1.1.0
+ set-function-name: 2.0.2
+
+ jiti@2.6.0: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@4.1.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsdom@27.0.1(postcss@8.5.6):
+ dependencies:
+ '@asamuzakjp/dom-selector': 6.7.3
+ cssstyle: 5.3.1(postcss@8.5.6)
+ data-urls: 6.0.0
+ decimal.js: 10.6.0
+ html-encoding-sniffer: 4.0.0
+ http-proxy-agent: 7.0.2
+ https-proxy-agent: 7.0.6
+ is-potential-custom-element-name: 1.0.1
+ parse5: 8.0.0
+ rrweb-cssom: 0.8.0
+ saxes: 6.0.0
+ symbol-tree: 3.2.4
+ tough-cookie: 6.0.0
+ w3c-xmlserializer: 5.0.0
+ webidl-conversions: 8.0.0
+ whatwg-encoding: 3.1.1
+ whatwg-mimetype: 4.0.0
+ whatwg-url: 15.1.0
+ ws: 8.18.3
+ xml-name-validator: 5.0.0
+ transitivePeerDependencies:
+ - bufferutil
+ - postcss
+ - supports-color
+ - utf-8-validate
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-parse-even-better-errors@2.3.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@1.0.2:
+ dependencies:
+ minimist: 1.2.8
+
+ json5@2.2.3: {}
+
+ jsx-ast-utils@3.3.5:
+ dependencies:
+ array-includes: 3.1.9
+ array.prototype.flat: 1.3.3
+ object.assign: 4.1.7
+ object.values: 1.2.1
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ language-subtag-registry@0.3.23: {}
+
+ language-tags@1.0.9:
+ dependencies:
+ language-subtag-registry: 0.3.23
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-darwin-arm64@1.30.1:
+ optional: true
+
+ lightningcss-darwin-x64@1.30.1:
+ optional: true
+
+ lightningcss-freebsd-x64@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.30.1:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.30.1:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.30.1:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.30.1:
+ optional: true
+
+ lightningcss@1.30.1:
+ dependencies:
+ detect-libc: 2.1.1
+ optionalDependencies:
+ lightningcss-darwin-arm64: 1.30.1
+ lightningcss-darwin-x64: 1.30.1
+ lightningcss-freebsd-x64: 1.30.1
+ lightningcss-linux-arm-gnueabihf: 1.30.1
+ lightningcss-linux-arm64-gnu: 1.30.1
+ lightningcss-linux-arm64-musl: 1.30.1
+ lightningcss-linux-x64-gnu: 1.30.1
+ lightningcss-linux-x64-musl: 1.30.1
+ lightningcss-win32-arm64-msvc: 1.30.1
+ lightningcss-win32-x64-msvc: 1.30.1
+
+ lines-and-columns@1.2.4: {}
+
+ linkify-it@5.0.0:
+ dependencies:
+ uc.micro: 2.1.0
+
+ linkifyjs@4.3.2: {}
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.debounce@4.0.8: {}
+
+ lodash.merge@4.6.2: {}
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ lower-case@2.0.2:
+ dependencies:
+ tslib: 2.8.1
+
+ lru-cache@11.2.2: {}
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ magic-string@0.30.19:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ markdown-it@14.1.0:
+ dependencies:
+ argparse: 2.0.1
+ entities: 4.5.0
+ linkify-it: 5.0.0
+ mdurl: 2.0.0
+ punycode.js: 2.3.1
+ uc.micro: 2.1.0
+
+ math-intrinsics@1.1.0: {}
+
+ mdn-data@2.0.28: {}
+
+ mdn-data@2.0.30: {}
+
+ mdn-data@2.12.2: {}
+
+ mdurl@2.0.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@5.1.6:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimatch@9.0.5:
+ dependencies:
+ brace-expansion: 2.0.2
+
+ minimist@1.2.8: {}
+
+ minipass@7.1.2: {}
+
+ minizlib@3.1.0:
+ dependencies:
+ minipass: 7.1.2
+
+ 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.3: {}
+
+ natural-compare@1.4.0: {}
+
+ next@16.0.10(@babel/core@7.28.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(sass@1.93.2):
+ dependencies:
+ '@next/env': 16.0.10
+ '@swc/helpers': 0.5.15
+ caniuse-lite: 1.0.30001746
+ postcss: 8.4.31
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+ styled-jsx: 5.1.6(@babel/core@7.28.4)(react@19.1.0)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 16.0.10
+ '@next/swc-darwin-x64': 16.0.10
+ '@next/swc-linux-arm64-gnu': 16.0.10
+ '@next/swc-linux-arm64-musl': 16.0.10
+ '@next/swc-linux-x64-gnu': 16.0.10
+ '@next/swc-linux-x64-musl': 16.0.10
+ '@next/swc-win32-arm64-msvc': 16.0.10
+ '@next/swc-win32-x64-msvc': 16.0.10
+ sass: 1.93.2
+ sharp: 0.34.4
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ no-case@3.0.4:
+ dependencies:
+ lower-case: 2.0.2
+ tslib: 2.8.1
+
+ node-addon-api@7.1.1:
+ optional: true
+
+ node-releases@2.0.23: {}
+
+ nth-check@2.1.1:
+ dependencies:
+ boolbase: 1.0.0
+
+ 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
+
+ once@1.4.0:
+ dependencies:
+ wrappy: 1.0.2
+
+ 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
+
+ orderedmap@2.1.1: {}
+
+ own-keys@1.0.1:
+ dependencies:
+ get-intrinsic: 1.3.1
+ object-keys: 1.1.1
+ safe-push-apply: 1.0.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ parse-json@5.2.0:
+ dependencies:
+ '@babel/code-frame': 7.27.1
+ error-ex: 1.3.4
+ json-parse-even-better-errors: 2.3.1
+ lines-and-columns: 1.2.4
+
+ parse5@8.0.0:
+ dependencies:
+ entities: 6.0.1
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ path-type@4.0.0: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.1: {}
+
+ picomatch@4.0.3: {}
+
+ possible-typed-array-names@1.1.0: {}
+
+ postcss-selector-parser@6.0.10:
+ dependencies:
+ cssesc: 3.0.0
+ util-deprecate: 1.0.2
+
+ 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-plugin-tailwindcss@0.6.14(prettier@3.6.2):
+ dependencies:
+ prettier: 3.6.2
+
+ prettier@2.8.8: {}
+
+ prettier@3.6.2: {}
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ prosemirror-changeset@2.3.1:
+ dependencies:
+ prosemirror-transform: 1.10.4
+
+ prosemirror-collab@1.3.1:
+ dependencies:
+ prosemirror-state: 1.4.3
+
+ prosemirror-commands@1.7.1:
+ dependencies:
+ prosemirror-model: 1.25.3
+ prosemirror-state: 1.4.3
+ prosemirror-transform: 1.10.4
+
+ prosemirror-dropcursor@1.8.2:
+ dependencies:
+ prosemirror-state: 1.4.3
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.41.3
+
+ prosemirror-gapcursor@1.4.0:
+ dependencies:
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.3
+ prosemirror-state: 1.4.3
+ prosemirror-view: 1.41.3
+
+ prosemirror-history@1.4.1:
+ dependencies:
+ prosemirror-state: 1.4.3
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.41.3
+ rope-sequence: 1.3.4
+
+ prosemirror-inputrules@1.5.1:
+ dependencies:
+ prosemirror-state: 1.4.3
+ prosemirror-transform: 1.10.4
+
+ prosemirror-keymap@1.2.3:
+ dependencies:
+ prosemirror-state: 1.4.3
+ w3c-keyname: 2.2.8
+
+ prosemirror-markdown@1.13.2:
+ dependencies:
+ '@types/markdown-it': 14.1.2
+ markdown-it: 14.1.0
+ prosemirror-model: 1.25.3
+
+ prosemirror-menu@1.2.5:
+ dependencies:
+ crelt: 1.0.6
+ prosemirror-commands: 1.7.1
+ prosemirror-history: 1.4.1
+ prosemirror-state: 1.4.3
+
+ prosemirror-model@1.25.3:
+ dependencies:
+ orderedmap: 2.1.1
+
+ prosemirror-schema-basic@1.2.4:
+ dependencies:
+ prosemirror-model: 1.25.3
+
+ prosemirror-schema-list@1.5.1:
+ dependencies:
+ prosemirror-model: 1.25.3
+ prosemirror-state: 1.4.3
+ prosemirror-transform: 1.10.4
+
+ prosemirror-state@1.4.3:
+ dependencies:
+ prosemirror-model: 1.25.3
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.41.3
+
+ prosemirror-tables@1.8.1:
+ dependencies:
+ prosemirror-keymap: 1.2.3
+ prosemirror-model: 1.25.3
+ prosemirror-state: 1.4.3
+ prosemirror-transform: 1.10.4
+ prosemirror-view: 1.41.3
+
+ prosemirror-trailing-node@3.0.0(prosemirror-model@1.25.3)(prosemirror-state@1.4.3)(prosemirror-view@1.41.3):
+ dependencies:
+ '@remirror/core-constants': 3.0.0
+ escape-string-regexp: 4.0.0
+ prosemirror-model: 1.25.3
+ prosemirror-state: 1.4.3
+ prosemirror-view: 1.41.3
+
+ prosemirror-transform@1.10.4:
+ dependencies:
+ prosemirror-model: 1.25.3
+
+ prosemirror-view@1.41.3:
+ dependencies:
+ prosemirror-model: 1.25.3
+ prosemirror-state: 1.4.3
+ prosemirror-transform: 1.10.4
+
+ proxy-from-env@1.1.0: {}
+
+ punycode.js@2.3.1: {}
+
+ punycode@2.3.1: {}
+
+ qs@6.14.0:
+ dependencies:
+ side-channel: 1.1.0
+
+ queue-microtask@1.2.3: {}
+
+ react-datepicker@8.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ '@floating-ui/react': 0.27.16(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
+ clsx: 2.1.1
+ date-fns: 4.1.0
+ react: 19.1.0
+ react-dom: 19.1.0(react@19.1.0)
+
+ react-dom@19.1.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ scheduler: 0.26.0
+
+ react-hook-form@7.66.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ react-intersection-observer@10.0.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+ optionalDependencies:
+ react-dom: 19.1.0(react@19.1.0)
+
+ react-is@16.13.1: {}
+
+ react@19.1.0: {}
+
+ readdirp@4.1.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.1
+ get-proto: 1.0.1
+ which-builtin-type: 1.2.1
+
+ regenerate-unicode-properties@10.2.2:
+ dependencies:
+ regenerate: 1.4.2
+
+ regenerate@1.4.2: {}
+
+ 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
+
+ regexpu-core@6.4.0:
+ dependencies:
+ regenerate: 1.4.2
+ regenerate-unicode-properties: 10.2.2
+ regjsgen: 0.8.0
+ regjsparser: 0.13.0
+ unicode-match-property-ecmascript: 2.0.0
+ unicode-match-property-value-ecmascript: 2.2.1
+
+ regjsgen@0.8.0: {}
+
+ regjsparser@0.13.0:
+ dependencies:
+ jsesc: 3.1.0
+
+ require-from-string@2.0.2: {}
+
+ resolve-from@4.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve@1.22.10:
+ 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: {}
+
+ rope-sequence@1.3.4: {}
+
+ rrweb-cssom@0.8.0: {}
+
+ 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.1
+ has-symbols: 1.1.0
+ isarray: 2.0.5
+
+ safe-push-apply@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ isarray: 2.0.5
+
+ safe-regex-test@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-regex: 1.2.1
+
+ safer-buffer@2.1.2: {}
+
+ sass@1.93.2:
+ dependencies:
+ chokidar: 4.0.3
+ immutable: 5.1.4
+ source-map-js: 1.2.1
+ optionalDependencies:
+ '@parcel/watcher': 2.5.1
+
+ saxes@6.0.0:
+ dependencies:
+ xmlchars: 2.2.0
+
+ scheduler@0.26.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.7.2: {}
+
+ 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.1
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ set-function-name@2.0.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.2
+
+ set-proto@1.0.0:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.1
+
+ sharp@0.34.4:
+ dependencies:
+ '@img/colour': 1.0.0
+ detect-libc: 2.1.1
+ semver: 7.7.2
+ 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.1
+ 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.1
+ 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
+
+ snake-case@3.0.4:
+ dependencies:
+ dot-case: 3.0.4
+ tslib: 2.8.1
+
+ source-map-js@1.2.1: {}
+
+ stable-hash@0.0.5: {}
+
+ stop-iteration-iterator@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ internal-slot: 1.1.0
+
+ string.prototype.includes@2.0.1:
+ dependencies:
+ call-bind: 1.0.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.1
+ 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
+
+ strip-bom@3.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ styled-jsx@5.1.6(@babel/core@7.28.4)(react@19.1.0):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.1.0
+ optionalDependencies:
+ '@babel/core': 7.28.4
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ svg-parser@2.0.4: {}
+
+ svgo@3.3.2:
+ dependencies:
+ '@trysound/sax': 0.2.0
+ commander: 7.2.0
+ css-select: 5.2.2
+ css-tree: 2.3.1
+ css-what: 6.2.2
+ csso: 5.0.5
+ picocolors: 1.1.1
+
+ swiper@12.0.3: {}
+
+ symbol-tree@3.2.4: {}
+
+ tabbable@6.2.0: {}
+
+ tailwind-merge@3.3.1: {}
+
+ tailwindcss@4.1.13: {}
+
+ tapable@2.2.3: {}
+
+ tar@7.5.1:
+ dependencies:
+ '@isaacs/fs-minipass': 4.0.1
+ chownr: 3.0.0
+ minipass: 7.1.2
+ minizlib: 3.1.0
+ yallist: 5.0.0
+
+ tinyglobby@0.2.15:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.3)
+ picomatch: 4.0.3
+
+ tldts-core@7.0.17: {}
+
+ tldts@7.0.17:
+ dependencies:
+ tldts-core: 7.0.17
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ tough-cookie@6.0.0:
+ dependencies:
+ tldts: 7.0.17
+
+ tr46@6.0.0:
+ dependencies:
+ punycode: 2.3.1
+
+ ts-api-utils@2.1.0(typescript@5.9.2):
+ dependencies:
+ typescript: 5.9.2
+
+ tsconfig-paths@3.15.0:
+ dependencies:
+ '@types/json5': 0.0.29
+ json5: 1.0.2
+ minimist: 1.2.8
+ strip-bom: 3.0.0
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typed-array-buffer@1.0.3:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-length@1.0.3:
+ dependencies:
+ call-bind: 1.0.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.2: {}
+
+ uc.micro@2.1.0: {}
+
+ 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: {}
+
+ unicode-canonical-property-names-ecmascript@2.0.1: {}
+
+ unicode-match-property-ecmascript@2.0.0:
+ dependencies:
+ unicode-canonical-property-names-ecmascript: 2.0.1
+ unicode-property-aliases-ecmascript: 2.2.0
+
+ unicode-match-property-value-ecmascript@2.2.1: {}
+
+ unicode-property-aliases-ecmascript@2.2.0: {}
+
+ unrs-resolver@1.11.1:
+ dependencies:
+ napi-postinstall: 0.3.3
+ optionalDependencies:
+ '@unrs/resolver-binding-android-arm-eabi': 1.11.1
+ '@unrs/resolver-binding-android-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-arm64': 1.11.1
+ '@unrs/resolver-binding-darwin-x64': 1.11.1
+ '@unrs/resolver-binding-freebsd-x64': 1.11.1
+ '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-arm64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1
+ '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-gnu': 1.11.1
+ '@unrs/resolver-binding-linux-x64-musl': 1.11.1
+ '@unrs/resolver-binding-wasm32-wasi': 1.11.1
+ '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1
+ '@unrs/resolver-binding-win32-x64-msvc': 1.11.1
+
+ update-browserslist-db@1.1.3(browserslist@4.26.3):
+ dependencies:
+ browserslist: 4.26.3
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ use-sync-external-store@1.6.0(react@19.1.0):
+ dependencies:
+ react: 19.1.0
+
+ util-deprecate@1.0.2: {}
+
+ w3c-keyname@2.2.8: {}
+
+ w3c-xmlserializer@5.0.0:
+ dependencies:
+ xml-name-validator: 5.0.0
+
+ webidl-conversions@8.0.0: {}
+
+ whatwg-encoding@3.1.1:
+ dependencies:
+ iconv-lite: 0.6.3
+
+ whatwg-mimetype@4.0.0: {}
+
+ whatwg-url@15.1.0:
+ dependencies:
+ tr46: 6.0.0
+ webidl-conversions: 8.0.0
+
+ 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.0
+ 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
+
+ word-wrap@1.2.5: {}
+
+ wrappy@1.0.2: {}
+
+ ws@8.18.3: {}
+
+ xml-name-validator@5.0.0: {}
+
+ xmlchars@2.2.0: {}
+
+ yallist@3.1.1: {}
+
+ yallist@5.0.0: {}
+
+ yocto-queue@0.1.0: {}
+
+ zod@4.1.12: {}
+
+ zustand@5.0.8(@types/react@19.1.16)(react@19.1.0)(use-sync-external-store@1.6.0(react@19.1.0)):
+ optionalDependencies:
+ '@types/react': 19.1.16
+ react: 19.1.0
+ use-sync-external-store: 1.6.0(react@19.1.0)
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/favicon.svg b/public/favicon.svg
new file mode 100644
index 00000000..bf5bbf06
--- /dev/null
+++ b/public/favicon.svg
@@ -0,0 +1,6 @@
+
\ No newline at end of file
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/icons/404.svg b/public/icons/404.svg
new file mode 100644
index 00000000..2780ad74
--- /dev/null
+++ b/public/icons/404.svg
@@ -0,0 +1,7 @@
+
diff --git a/public/icons/arrow-down-gray.svg b/public/icons/arrow-down-gray.svg
new file mode 100644
index 00000000..30705e24
--- /dev/null
+++ b/public/icons/arrow-down-gray.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/arrow-down-primary.svg b/public/icons/arrow-down-primary.svg
new file mode 100644
index 00000000..71024c9d
--- /dev/null
+++ b/public/icons/arrow-down-primary.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/arrow-left.svg b/public/icons/arrow-left.svg
new file mode 100644
index 00000000..2fcdeb75
--- /dev/null
+++ b/public/icons/arrow-left.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/arrow-right.svg b/public/icons/arrow-right.svg
new file mode 100644
index 00000000..b3a2b04f
--- /dev/null
+++ b/public/icons/arrow-right.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/arrow-up-gray.svg b/public/icons/arrow-up-gray.svg
new file mode 100644
index 00000000..8cbbd8b4
--- /dev/null
+++ b/public/icons/arrow-up-gray.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/arrow-up-primary.svg b/public/icons/arrow-up-primary.svg
new file mode 100644
index 00000000..bbc24b45
--- /dev/null
+++ b/public/icons/arrow-up-primary.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/icons/banner-mobile.png b/public/icons/banner-mobile.png
new file mode 100644
index 00000000..2e2a21ef
Binary files /dev/null and b/public/icons/banner-mobile.png differ
diff --git a/public/icons/banner.svg b/public/icons/banner.svg
new file mode 100644
index 00000000..7153a2f1
--- /dev/null
+++ b/public/icons/banner.svg
@@ -0,0 +1,59 @@
+
diff --git a/public/icons/behance.svg b/public/icons/behance.svg
new file mode 100644
index 00000000..4341e072
--- /dev/null
+++ b/public/icons/behance.svg
@@ -0,0 +1,14 @@
+
diff --git a/public/icons/calendar.svg b/public/icons/calendar.svg
new file mode 100644
index 00000000..1d4c6036
--- /dev/null
+++ b/public/icons/calendar.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/close.svg b/public/icons/close.svg
new file mode 100644
index 00000000..c78f8186
--- /dev/null
+++ b/public/icons/close.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/date-left.svg b/public/icons/date-left.svg
new file mode 100644
index 00000000..970d792f
--- /dev/null
+++ b/public/icons/date-left.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/date-right.svg b/public/icons/date-right.svg
new file mode 100644
index 00000000..f3cb8afd
--- /dev/null
+++ b/public/icons/date-right.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/disabled_arrow_left.svg b/public/icons/disabled_arrow_left.svg
new file mode 100644
index 00000000..e6d9c27c
--- /dev/null
+++ b/public/icons/disabled_arrow_left.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/disabled_arrow_right.svg b/public/icons/disabled_arrow_right.svg
new file mode 100644
index 00000000..1c7f1715
--- /dev/null
+++ b/public/icons/disabled_arrow_right.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/double-arrow-left.svg b/public/icons/double-arrow-left.svg
new file mode 100644
index 00000000..7b27c739
--- /dev/null
+++ b/public/icons/double-arrow-left.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/double-arrow-right.svg b/public/icons/double-arrow-right.svg
new file mode 100644
index 00000000..49c62712
--- /dev/null
+++ b/public/icons/double-arrow-right.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/edit-black.svg b/public/icons/edit-black.svg
new file mode 100644
index 00000000..ac2e13b1
--- /dev/null
+++ b/public/icons/edit-black.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/edit-selected.svg b/public/icons/edit-selected.svg
new file mode 100644
index 00000000..94153c0e
--- /dev/null
+++ b/public/icons/edit-selected.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/edit.svg b/public/icons/edit.svg
new file mode 100644
index 00000000..5229be9c
--- /dev/null
+++ b/public/icons/edit.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/editor/text-bold-selected.svg b/public/icons/editor/text-bold-selected.svg
new file mode 100644
index 00000000..e56d0a38
--- /dev/null
+++ b/public/icons/editor/text-bold-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-bold.svg b/public/icons/editor/text-bold.svg
new file mode 100644
index 00000000..67722703
--- /dev/null
+++ b/public/icons/editor/text-bold.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-h1-selected.svg b/public/icons/editor/text-h1-selected.svg
new file mode 100644
index 00000000..8295bf4b
--- /dev/null
+++ b/public/icons/editor/text-h1-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-h1.svg b/public/icons/editor/text-h1.svg
new file mode 100644
index 00000000..c2b90245
--- /dev/null
+++ b/public/icons/editor/text-h1.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-h2-selected.svg b/public/icons/editor/text-h2-selected.svg
new file mode 100644
index 00000000..d248dd8a
--- /dev/null
+++ b/public/icons/editor/text-h2-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-h2.svg b/public/icons/editor/text-h2.svg
new file mode 100644
index 00000000..5e3724c4
--- /dev/null
+++ b/public/icons/editor/text-h2.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-h3-selected.svg b/public/icons/editor/text-h3-selected.svg
new file mode 100644
index 00000000..2be41891
--- /dev/null
+++ b/public/icons/editor/text-h3-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-h3.svg b/public/icons/editor/text-h3.svg
new file mode 100644
index 00000000..f72cb7b1
--- /dev/null
+++ b/public/icons/editor/text-h3.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-italic-selected.svg b/public/icons/editor/text-italic-selected.svg
new file mode 100644
index 00000000..27331b6d
--- /dev/null
+++ b/public/icons/editor/text-italic-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-italic.svg b/public/icons/editor/text-italic.svg
new file mode 100644
index 00000000..e17cebd5
--- /dev/null
+++ b/public/icons/editor/text-italic.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-link-selected.svg b/public/icons/editor/text-link-selected.svg
new file mode 100644
index 00000000..9d811770
--- /dev/null
+++ b/public/icons/editor/text-link-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-link.svg b/public/icons/editor/text-link.svg
new file mode 100644
index 00000000..8975c2a6
--- /dev/null
+++ b/public/icons/editor/text-link.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-ol-selected.svg b/public/icons/editor/text-ol-selected.svg
new file mode 100644
index 00000000..52b695b7
--- /dev/null
+++ b/public/icons/editor/text-ol-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-ol.svg b/public/icons/editor/text-ol.svg
new file mode 100644
index 00000000..bd983af5
--- /dev/null
+++ b/public/icons/editor/text-ol.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-ul-selected.svg b/public/icons/editor/text-ul-selected.svg
new file mode 100644
index 00000000..99b2d54d
--- /dev/null
+++ b/public/icons/editor/text-ul-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-ul.svg b/public/icons/editor/text-ul.svg
new file mode 100644
index 00000000..d0fc956d
--- /dev/null
+++ b/public/icons/editor/text-ul.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-underline-selected.svg b/public/icons/editor/text-underline-selected.svg
new file mode 100644
index 00000000..9c1e211c
--- /dev/null
+++ b/public/icons/editor/text-underline-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/editor/text-underline.svg b/public/icons/editor/text-underline.svg
new file mode 100644
index 00000000..77310ab1
--- /dev/null
+++ b/public/icons/editor/text-underline.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/empty-link.svg b/public/icons/empty-link.svg
new file mode 100644
index 00000000..a00c0a24
--- /dev/null
+++ b/public/icons/empty-link.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/etclink.svg b/public/icons/etclink.svg
new file mode 100644
index 00000000..685630df
--- /dev/null
+++ b/public/icons/etclink.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/icons/first.svg b/public/icons/first.svg
new file mode 100644
index 00000000..ad12fe88
--- /dev/null
+++ b/public/icons/first.svg
@@ -0,0 +1,7 @@
+
diff --git a/public/icons/footer-logo.svg b/public/icons/footer-logo.svg
new file mode 100644
index 00000000..659030f4
--- /dev/null
+++ b/public/icons/footer-logo.svg
@@ -0,0 +1,12 @@
+
diff --git a/public/icons/github.svg b/public/icons/github.svg
new file mode 100644
index 00000000..a9cf9d63
--- /dev/null
+++ b/public/icons/github.svg
@@ -0,0 +1,14 @@
+
diff --git a/public/icons/google.svg b/public/icons/google.svg
new file mode 100644
index 00000000..685db65b
--- /dev/null
+++ b/public/icons/google.svg
@@ -0,0 +1,6 @@
+
diff --git a/public/icons/gray_teamficial_symbol.svg b/public/icons/gray_teamficial_symbol.svg
new file mode 100644
index 00000000..469c76f3
--- /dev/null
+++ b/public/icons/gray_teamficial_symbol.svg
@@ -0,0 +1,6 @@
+
diff --git a/public/icons/header-arrow-left.svg b/public/icons/header-arrow-left.svg
new file mode 100644
index 00000000..d93cc7fe
--- /dev/null
+++ b/public/icons/header-arrow-left.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/initial-profile.svg b/public/icons/initial-profile.svg
new file mode 100644
index 00000000..826880de
--- /dev/null
+++ b/public/icons/initial-profile.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/instagram.svg b/public/icons/instagram.svg
new file mode 100644
index 00000000..4df8d3bb
--- /dev/null
+++ b/public/icons/instagram.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/kakao.svg b/public/icons/kakao.svg
new file mode 100644
index 00000000..43c10766
--- /dev/null
+++ b/public/icons/kakao.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/icons/linkedin.svg b/public/icons/linkedin.svg
new file mode 100644
index 00000000..65ed8e35
--- /dev/null
+++ b/public/icons/linkedin.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/mail.svg b/public/icons/mail.svg
new file mode 100644
index 00000000..f8a6b3b5
--- /dev/null
+++ b/public/icons/mail.svg
@@ -0,0 +1,15 @@
+
diff --git a/public/icons/menu.svg b/public/icons/menu.svg
new file mode 100644
index 00000000..46aaea0f
--- /dev/null
+++ b/public/icons/menu.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/minus-disabled.svg b/public/icons/minus-disabled.svg
new file mode 100644
index 00000000..370be68f
--- /dev/null
+++ b/public/icons/minus-disabled.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/minus.svg b/public/icons/minus.svg
new file mode 100644
index 00000000..b80c1715
--- /dev/null
+++ b/public/icons/minus.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/mobile-instagram.svg b/public/icons/mobile-instagram.svg
new file mode 100644
index 00000000..d3ce19eb
--- /dev/null
+++ b/public/icons/mobile-instagram.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/more.svg b/public/icons/more.svg
new file mode 100644
index 00000000..39b6f366
--- /dev/null
+++ b/public/icons/more.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/mypage-right-arrow.svg b/public/icons/mypage-right-arrow.svg
new file mode 100644
index 00000000..e175e526
--- /dev/null
+++ b/public/icons/mypage-right-arrow.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/naver.svg b/public/icons/naver.svg
new file mode 100644
index 00000000..eeb2db16
--- /dev/null
+++ b/public/icons/naver.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/notion.svg b/public/icons/notion.svg
new file mode 100644
index 00000000..323b187a
--- /dev/null
+++ b/public/icons/notion.svg
@@ -0,0 +1,14 @@
+
diff --git a/public/icons/page-after.svg b/public/icons/page-after.svg
new file mode 100644
index 00000000..7ee19e8b
--- /dev/null
+++ b/public/icons/page-after.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/page-before.svg b/public/icons/page-before.svg
new file mode 100644
index 00000000..2c5d05eb
--- /dev/null
+++ b/public/icons/page-before.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/plus.svg b/public/icons/plus.svg
new file mode 100644
index 00000000..3baa81c0
--- /dev/null
+++ b/public/icons/plus.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/profile-add.svg b/public/icons/profile-add.svg
new file mode 100644
index 00000000..f185ef4e
--- /dev/null
+++ b/public/icons/profile-add.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/icons/profile-image-add.svg b/public/icons/profile-image-add.svg
new file mode 100644
index 00000000..b264e58a
--- /dev/null
+++ b/public/icons/profile-image-add.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/profile-link.svg b/public/icons/profile-link.svg
new file mode 100644
index 00000000..8d851ed5
--- /dev/null
+++ b/public/icons/profile-link.svg
@@ -0,0 +1,10 @@
+
diff --git a/public/icons/profile-mobile-trash.svg b/public/icons/profile-mobile-trash.svg
new file mode 100644
index 00000000..ec7d039d
--- /dev/null
+++ b/public/icons/profile-mobile-trash.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/icons/profile.svg b/public/icons/profile.svg
new file mode 100644
index 00000000..340f9a93
--- /dev/null
+++ b/public/icons/profile.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/profile_arrow_left.svg b/public/icons/profile_arrow_left.svg
new file mode 100644
index 00000000..4070e4e7
--- /dev/null
+++ b/public/icons/profile_arrow_left.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/profile_arrow_right.svg b/public/icons/profile_arrow_right.svg
new file mode 100644
index 00000000..ff052e2d
--- /dev/null
+++ b/public/icons/profile_arrow_right.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/profile_dropdown_down.svg b/public/icons/profile_dropdown_down.svg
new file mode 100644
index 00000000..f1fd0c51
--- /dev/null
+++ b/public/icons/profile_dropdown_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/profile_dropdown_up.svg b/public/icons/profile_dropdown_up.svg
new file mode 100644
index 00000000..720c30fc
--- /dev/null
+++ b/public/icons/profile_dropdown_up.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/question-border.svg b/public/icons/question-border.svg
new file mode 100644
index 00000000..1e13268d
--- /dev/null
+++ b/public/icons/question-border.svg
@@ -0,0 +1,18 @@
+
diff --git a/public/icons/question.svg b/public/icons/question.svg
new file mode 100644
index 00000000..73524e08
--- /dev/null
+++ b/public/icons/question.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/question_down.svg b/public/icons/question_down.svg
new file mode 100644
index 00000000..0d93211d
--- /dev/null
+++ b/public/icons/question_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/question_gray.svg b/public/icons/question_gray.svg
new file mode 100644
index 00000000..96a47f30
--- /dev/null
+++ b/public/icons/question_gray.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/question_gray_down.svg b/public/icons/question_gray_down.svg
new file mode 100644
index 00000000..6b0a6785
--- /dev/null
+++ b/public/icons/question_gray_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/question_gray_up.svg b/public/icons/question_gray_up.svg
new file mode 100644
index 00000000..a7d20d8b
--- /dev/null
+++ b/public/icons/question_gray_up.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/question_up.svg b/public/icons/question_up.svg
new file mode 100644
index 00000000..f5107f37
--- /dev/null
+++ b/public/icons/question_up.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/radio-selected.svg b/public/icons/radio-selected.svg
new file mode 100644
index 00000000..9ac76b60
--- /dev/null
+++ b/public/icons/radio-selected.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/radio-unselected.svg b/public/icons/radio-unselected.svg
new file mode 100644
index 00000000..0275a734
--- /dev/null
+++ b/public/icons/radio-unselected.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/recent.svg b/public/icons/recent.svg
new file mode 100644
index 00000000..f9006c63
--- /dev/null
+++ b/public/icons/recent.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/icons/second.svg b/public/icons/second.svg
new file mode 100644
index 00000000..8eaeeace
--- /dev/null
+++ b/public/icons/second.svg
@@ -0,0 +1,7 @@
+
diff --git a/public/icons/share-black.svg b/public/icons/share-black.svg
new file mode 100644
index 00000000..f90b903d
--- /dev/null
+++ b/public/icons/share-black.svg
@@ -0,0 +1,4 @@
+
diff --git a/public/icons/share.svg b/public/icons/share.svg
new file mode 100644
index 00000000..47a5c023
--- /dev/null
+++ b/public/icons/share.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/siren.svg b/public/icons/siren.svg
new file mode 100644
index 00000000..45deea54
--- /dev/null
+++ b/public/icons/siren.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/teamficial-header.svg b/public/icons/teamficial-header.svg
new file mode 100644
index 00000000..0d034a26
--- /dev/null
+++ b/public/icons/teamficial-header.svg
@@ -0,0 +1,22 @@
+
diff --git a/public/icons/teamficial-keyword.svg b/public/icons/teamficial-keyword.svg
new file mode 100644
index 00000000..bc45f2ba
--- /dev/null
+++ b/public/icons/teamficial-keyword.svg
@@ -0,0 +1,16 @@
+
diff --git a/public/icons/teamficial-login.svg b/public/icons/teamficial-login.svg
new file mode 100644
index 00000000..bb6229da
--- /dev/null
+++ b/public/icons/teamficial-login.svg
@@ -0,0 +1,655 @@
+
+
\ No newline at end of file
diff --git a/public/icons/teamficial-logo.svg b/public/icons/teamficial-logo.svg
new file mode 100644
index 00000000..c6f6ce90
--- /dev/null
+++ b/public/icons/teamficial-logo.svg
@@ -0,0 +1,5 @@
+
diff --git a/public/icons/teamficial-symbol.svg b/public/icons/teamficial-symbol.svg
new file mode 100644
index 00000000..7e10d204
--- /dev/null
+++ b/public/icons/teamficial-symbol.svg
@@ -0,0 +1,8 @@
+
diff --git a/public/icons/toast-error.svg b/public/icons/toast-error.svg
new file mode 100644
index 00000000..3377bd00
--- /dev/null
+++ b/public/icons/toast-error.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/tutorial-1.svg b/public/icons/tutorial-1.svg
new file mode 100644
index 00000000..34932dd8
--- /dev/null
+++ b/public/icons/tutorial-1.svg
@@ -0,0 +1,17 @@
+
diff --git a/public/icons/tutorial-2.svg b/public/icons/tutorial-2.svg
new file mode 100644
index 00000000..a737a88a
--- /dev/null
+++ b/public/icons/tutorial-2.svg
@@ -0,0 +1,12 @@
+
diff --git a/public/icons/tutorial.svg b/public/icons/tutorial.svg
new file mode 100644
index 00000000..c835b1ec
--- /dev/null
+++ b/public/icons/tutorial.svg
@@ -0,0 +1,27 @@
+
diff --git a/public/icons/x-button.svg b/public/icons/x-button.svg
new file mode 100644
index 00000000..3153169a
--- /dev/null
+++ b/public/icons/x-button.svg
@@ -0,0 +1,3 @@
+
diff --git a/public/icons/x-small-button.svg b/public/icons/x-small-button.svg
new file mode 100644
index 00000000..e7911129
--- /dev/null
+++ b/public/icons/x-small-button.svg
@@ -0,0 +1,4 @@
+
diff --git "a/public/images/keywords/\352\260\210\353\223\261\354\235\204 \354\241\260\354\240\225\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\260\210\353\223\261\354\235\204 \354\241\260\354\240\225\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..5e675f78
--- /dev/null
+++ "b/public/images/keywords/\352\260\210\353\223\261\354\235\204 \354\241\260\354\240\225\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\210\353\223\261\354\235\204 \355\202\244\354\232\260\354\247\200 \354\225\212\353\212\224.svg" "b/public/images/keywords/\352\260\210\353\223\261\354\235\204 \355\202\244\354\232\260\354\247\200 \354\225\212\353\212\224.svg"
new file mode 100644
index 00000000..120d8256
--- /dev/null
+++ "b/public/images/keywords/\352\260\210\353\223\261\354\235\204 \355\202\244\354\232\260\354\247\200 \354\225\212\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\210\355\224\274\353\245\274 \353\252\273 \354\236\241\353\212\224.svg" "b/public/images/keywords/\352\260\210\355\224\274\353\245\274 \353\252\273 \354\236\241\353\212\224.svg"
new file mode 100644
index 00000000..f3a0865d
--- /dev/null
+++ "b/public/images/keywords/\352\260\210\355\224\274\353\245\274 \353\252\273 \354\236\241\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\352\260\201\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\260\220\352\260\201\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..59d80a23
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\352\260\201\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225 \352\270\260\353\263\265\354\235\264 \354\236\210\353\212\224.svg" "b/public/images/keywords/\352\260\220\354\240\225 \352\270\260\353\263\265\354\235\264 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..7661f7b6
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225 \352\270\260\353\263\265\354\235\264 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225 \354\206\214\353\252\250\352\260\200 \354\236\210\353\212\224.svg" "b/public/images/keywords/\352\260\220\354\240\225 \354\206\214\353\252\250\352\260\200 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..f5fd3fea
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225 \354\206\214\353\252\250\352\260\200 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225 \354\206\214\353\252\250\353\245\274 \354\244\204\354\235\264\353\212\224.svg" "b/public/images/keywords/\352\260\220\354\240\225 \354\206\214\353\252\250\353\245\274 \354\244\204\354\235\264\353\212\224.svg"
new file mode 100644
index 00000000..bad7e879
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225 \354\206\214\353\252\250\353\245\274 \354\244\204\354\235\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225 \354\241\260\354\240\210\354\235\264 \354\236\230 \353\220\230\353\212\224.svg" "b/public/images/keywords/\352\260\220\354\240\225 \354\241\260\354\240\210\354\235\264 \354\236\230 \353\220\230\353\212\224.svg"
new file mode 100644
index 00000000..bfcaaa92
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225 \354\241\260\354\240\210\354\235\264 \354\236\230 \353\220\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225 \355\221\234\355\230\204\354\235\264 \354\204\234\355\210\260.svg" "b/public/images/keywords/\352\260\220\354\240\225 \355\221\234\355\230\204\354\235\264 \354\204\234\355\210\260.svg"
new file mode 100644
index 00000000..10a658a8
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225 \355\221\234\355\230\204\354\235\264 \354\204\234\355\210\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225 \355\221\234\355\230\204\354\235\264 \354\240\210\354\240\234\353\220\234.svg" "b/public/images/keywords/\352\260\220\354\240\225 \355\221\234\355\230\204\354\235\264 \354\240\210\354\240\234\353\220\234.svg"
new file mode 100644
index 00000000..65554502
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225 \355\221\234\355\230\204\354\235\264 \354\240\210\354\240\234\353\220\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225\354\235\204 \353\260\260\353\240\244\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\260\220\354\240\225\354\235\204 \353\260\260\353\240\244\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..92b63792
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225\354\235\204 \353\260\260\353\240\244\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\220\354\240\225\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\260\220\354\240\225\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..aa5500b1
--- /dev/null
+++ "b/public/images/keywords/\352\260\220\354\240\225\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\260\225\354\240\220\354\235\204 \354\236\230 \355\231\234\354\232\251\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\260\225\354\240\220\354\235\204 \354\236\230 \355\231\234\354\232\251\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..4aa26118
--- /dev/null
+++ "b/public/images/keywords/\352\260\225\354\240\220\354\235\204 \354\236\230 \355\231\234\354\232\251\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\262\260\352\263\274 \354\247\221\354\260\251\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\262\260\352\263\274 \354\247\221\354\260\251\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..29c628b2
--- /dev/null
+++ "b/public/images/keywords/\352\262\260\352\263\274 \354\247\221\354\260\251\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\262\260\352\263\274\353\245\274 \354\240\225\353\246\254\355\225\234.svg" "b/public/images/keywords/\352\262\260\352\263\274\353\245\274 \354\240\225\353\246\254\355\225\234.svg"
new file mode 100644
index 00000000..aa9aae7d
--- /dev/null
+++ "b/public/images/keywords/\352\262\260\352\263\274\353\245\274 \354\240\225\353\246\254\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\262\260\353\213\250\353\240\245\354\235\264 \354\225\275\355\225\234.svg" "b/public/images/keywords/\352\262\260\353\213\250\353\240\245\354\235\264 \354\225\275\355\225\234.svg"
new file mode 100644
index 00000000..7e4900d7
--- /dev/null
+++ "b/public/images/keywords/\352\262\260\353\213\250\353\240\245\354\235\264 \354\225\275\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\262\260\353\241\240\354\235\264 \353\212\220\353\246\260.svg" "b/public/images/keywords/\352\262\260\353\241\240\354\235\264 \353\212\220\353\246\260.svg"
new file mode 100644
index 00000000..61226c2f
--- /dev/null
+++ "b/public/images/keywords/\352\262\260\353\241\240\354\235\264 \353\212\220\353\246\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\262\260\354\240\225\354\235\264 \355\225\251\353\246\254\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\262\260\354\240\225\354\235\264 \355\225\251\353\246\254\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..7348e886
--- /dev/null
+++ "b/public/images/keywords/\352\262\260\354\240\225\354\235\264 \355\225\251\353\246\254\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\262\275\354\262\255\354\235\204 \354\236\230 \355\225\230\353\212\224.svg" "b/public/images/keywords/\352\262\275\354\262\255\354\235\204 \354\236\230 \355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..116832fa
--- /dev/null
+++ "b/public/images/keywords/\352\262\275\354\262\255\354\235\204 \354\236\230 \355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\204\355\232\215\353\263\264\353\213\244 \355\226\211\353\217\231\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\263\204\355\232\215\353\263\264\353\213\244 \355\226\211\353\217\231\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..790d9fdd
--- /dev/null
+++ "b/public/images/keywords/\352\263\204\355\232\215\353\263\264\353\213\244 \355\226\211\353\217\231\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\204\355\232\215\354\204\261\354\235\264 \353\266\200\354\241\261\355\225\234.svg" "b/public/images/keywords/\352\263\204\355\232\215\354\204\261\354\235\264 \353\266\200\354\241\261\355\225\234.svg"
new file mode 100644
index 00000000..c267c103
--- /dev/null
+++ "b/public/images/keywords/\352\263\204\355\232\215\354\204\261\354\235\264 \353\266\200\354\241\261\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\204\355\232\215\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\263\204\355\232\215\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..cebe8449
--- /dev/null
+++ "b/public/images/keywords/\352\263\204\355\232\215\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\240\353\257\274\354\235\264 \353\247\216\354\235\200.svg" "b/public/images/keywords/\352\263\240\353\257\274\354\235\264 \353\247\216\354\235\200.svg"
new file mode 100644
index 00000000..15f13b4f
--- /dev/null
+++ "b/public/images/keywords/\352\263\240\353\257\274\354\235\264 \353\247\216\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\240\354\247\221\354\212\244\353\237\254\354\232\264.svg" "b/public/images/keywords/\352\263\240\354\247\221\354\212\244\353\237\254\354\232\264.svg"
new file mode 100644
index 00000000..3dc98b27
--- /dev/null
+++ "b/public/images/keywords/\352\263\240\354\247\221\354\212\244\353\237\254\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\265\352\260\220 \353\212\245\353\240\245\354\235\264 \353\206\222\354\235\200.svg" "b/public/images/keywords/\352\263\265\352\260\220 \353\212\245\353\240\245\354\235\264 \353\206\222\354\235\200.svg"
new file mode 100644
index 00000000..8f9e1cfe
--- /dev/null
+++ "b/public/images/keywords/\352\263\265\352\260\220 \353\212\245\353\240\245\354\235\264 \353\206\222\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\265\352\260\220 \355\221\234\355\230\204\354\235\264 \354\240\201\354\235\200.svg" "b/public/images/keywords/\352\263\265\352\260\220 \355\221\234\355\230\204\354\235\264 \354\240\201\354\235\200.svg"
new file mode 100644
index 00000000..72c2650c
--- /dev/null
+++ "b/public/images/keywords/\352\263\265\352\260\220 \355\221\234\355\230\204\354\235\264 \354\240\201\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\274\353\217\204\355\225\230\352\262\214 \352\263\240\353\257\274\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\263\274\353\217\204\355\225\230\352\262\214 \352\263\240\353\257\274\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..86caacdb
--- /dev/null
+++ "b/public/images/keywords/\352\263\274\353\217\204\355\225\230\352\262\214 \352\263\240\353\257\274\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\274\353\252\260\354\236\205\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\263\274\353\252\260\354\236\205\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..0b373706
--- /dev/null
+++ "b/public/images/keywords/\352\263\274\353\252\260\354\236\205\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\263\274\354\236\245 \354\227\206\354\235\264 \353\247\220\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\263\274\354\236\245 \354\227\206\354\235\264 \353\247\220\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..aff8848e
--- /dev/null
+++ "b/public/images/keywords/\352\263\274\354\236\245 \354\227\206\354\235\264 \353\247\220\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\264\200\352\263\204\353\245\274 \354\206\214\354\244\221\355\236\210 \355\225\230\353\212\224.svg" "b/public/images/keywords/\352\264\200\352\263\204\353\245\274 \354\206\214\354\244\221\355\236\210 \355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..91802ff4
--- /dev/null
+++ "b/public/images/keywords/\352\264\200\352\263\204\353\245\274 \354\206\214\354\244\221\355\236\210 \355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\264\200\352\263\204\353\263\264\353\213\244 \352\262\260\352\263\274\355\230\225\354\235\270.svg" "b/public/images/keywords/\352\264\200\352\263\204\353\263\264\353\213\244 \352\262\260\352\263\274\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..e2da55fa
--- /dev/null
+++ "b/public/images/keywords/\352\264\200\352\263\204\353\263\264\353\213\244 \352\262\260\352\263\274\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\264\200\354\260\260\353\240\245\354\235\264 \354\242\213\354\235\200.svg" "b/public/images/keywords/\352\264\200\354\260\260\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
new file mode 100644
index 00000000..20439451
--- /dev/null
+++ "b/public/images/keywords/\352\264\200\354\260\260\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\265\254\354\241\260\353\245\274 \353\271\240\353\245\264\352\262\214 \355\214\214\354\225\205\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\265\254\354\241\260\353\245\274 \353\271\240\353\245\264\352\262\214 \355\214\214\354\225\205\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..a0e33833
--- /dev/null
+++ "b/public/images/keywords/\352\265\254\354\241\260\353\245\274 \353\271\240\353\245\264\352\262\214 \355\214\214\354\225\205\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\265\254\354\241\260\354\240\201\354\234\274\353\241\234 \354\203\235\352\260\201\355\225\230\353\212\224.svg" "b/public/images/keywords/\352\265\254\354\241\260\354\240\201\354\234\274\353\241\234 \354\203\235\352\260\201\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..29b59f21
--- /dev/null
+++ "b/public/images/keywords/\352\265\254\354\241\260\354\240\201\354\234\274\353\241\234 \354\203\235\352\260\201\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\267\240\355\230\225 \354\236\210\353\212\224.svg" "b/public/images/keywords/\352\267\240\355\230\225 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..15a5c71c
--- /dev/null
+++ "b/public/images/keywords/\352\267\240\355\230\225 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\267\240\355\230\225\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\267\240\355\230\225\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..c49ca888
--- /dev/null
+++ "b/public/images/keywords/\352\267\240\355\230\225\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\267\274\353\263\270 \354\233\220\354\235\270\354\235\204 \354\260\276\353\212\224.svg" "b/public/images/keywords/\352\267\274\353\263\270 \354\233\220\354\235\270\354\235\204 \354\260\276\353\212\224.svg"
new file mode 100644
index 00000000..1bbffbaf
--- /dev/null
+++ "b/public/images/keywords/\352\267\274\353\263\270 \354\233\220\354\235\270\354\235\204 \354\260\276\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\215\354\240\225\354\240\201 \354\225\225\353\260\225\354\235\204 \354\243\274\353\212\224.svg" "b/public/images/keywords/\352\270\215\354\240\225\354\240\201 \354\225\225\353\260\225\354\235\204 \354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..0ed0a0b2
--- /dev/null
+++ "b/public/images/keywords/\352\270\215\354\240\225\354\240\201 \354\225\225\353\260\225\354\235\204 \354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\215\354\240\225\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\270\215\354\240\225\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..003d96f4
--- /dev/null
+++ "b/public/images/keywords/\352\270\215\354\240\225\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\354\244\200 \354\244\221\354\213\254\354\240\201\354\235\270.svg" "b/public/images/keywords/\352\270\260\354\244\200 \354\244\221\354\213\254\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..3ce1f1f4
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\354\244\200 \354\244\221\354\213\254\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\354\244\200\354\235\204 \353\206\222\354\227\254\354\243\274\353\212\224.svg" "b/public/images/keywords/\352\270\260\354\244\200\354\235\204 \353\206\222\354\227\254\354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..c7c6d4a2
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\354\244\200\354\235\204 \353\206\222\354\227\254\354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\354\244\200\354\235\204 \354\204\270\354\233\214\354\243\274\353\212\224.svg" "b/public/images/keywords/\352\270\260\354\244\200\354\235\204 \354\204\270\354\233\214\354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..602887d3
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\354\244\200\354\235\204 \354\204\270\354\233\214\354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\354\244\200\354\235\204 \354\247\200\354\274\234\354\230\250.svg" "b/public/images/keywords/\352\270\260\354\244\200\354\235\204 \354\247\200\354\274\234\354\230\250.svg"
new file mode 100644
index 00000000..041d4ad8
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\354\244\200\354\235\204 \354\247\200\354\274\234\354\230\250.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\354\244\200\354\235\264 \353\206\222\354\235\200.svg" "b/public/images/keywords/\352\270\260\354\244\200\354\235\264 \353\206\222\354\235\200.svg"
new file mode 100644
index 00000000..05506c95
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\354\244\200\354\235\264 \353\206\222\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\354\244\200\354\235\264 \353\266\204\353\252\205\355\225\234.svg" "b/public/images/keywords/\352\270\260\354\244\200\354\235\264 \353\266\204\353\252\205\355\225\234.svg"
new file mode 100644
index 00000000..8dfaf2b9
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\354\244\200\354\235\264 \353\266\204\353\252\205\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\354\244\200\354\235\264 \355\235\224\353\223\244\353\246\254\354\247\200 \354\225\212\353\212\224.svg" "b/public/images/keywords/\352\270\260\354\244\200\354\235\264 \355\235\224\353\223\244\353\246\254\354\247\200 \354\225\212\353\212\224.svg"
new file mode 100644
index 00000000..7ef786e0
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\354\244\200\354\235\264 \355\235\224\353\223\244\353\246\254\354\247\200 \354\225\212\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\260\355\232\215\353\240\245\354\235\264 \354\242\213\354\235\200.svg" "b/public/images/keywords/\352\270\260\355\232\215\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
new file mode 100644
index 00000000..6a023d1c
--- /dev/null
+++ "b/public/images/keywords/\352\270\260\355\232\215\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\270\264\354\236\245\354\227\220 \353\257\274\352\260\220\355\225\234.svg" "b/public/images/keywords/\352\270\264\354\236\245\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
new file mode 100644
index 00000000..445655f2
--- /dev/null
+++ "b/public/images/keywords/\352\270\264\354\236\245\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\274\274\352\274\274\355\225\234.svg" "b/public/images/keywords/\352\274\274\352\274\274\355\225\234.svg"
new file mode 100644
index 00000000..bedea8ad
--- /dev/null
+++ "b/public/images/keywords/\352\274\274\352\274\274\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\276\270\354\244\200\355\225\234.svg" "b/public/images/keywords/\352\276\270\354\244\200\355\225\234.svg"
new file mode 100644
index 00000000..98f7186d
--- /dev/null
+++ "b/public/images/keywords/\352\276\270\354\244\200\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\352\276\270\354\244\200\355\225\250\354\235\264 \353\217\213\353\263\264\354\235\264\353\212\224.svg" "b/public/images/keywords/\352\276\270\354\244\200\355\225\250\354\235\264 \353\217\213\353\263\264\354\235\264\353\212\224.svg"
new file mode 100644
index 00000000..ac9e802d
--- /dev/null
+++ "b/public/images/keywords/\352\276\270\354\244\200\355\225\250\354\235\264 \353\217\213\353\263\264\354\235\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\201\210\352\270\260 \354\236\210\353\212\224.svg" "b/public/images/keywords/\353\201\210\352\270\260 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..20a7761f
--- /dev/null
+++ "b/public/images/keywords/\353\201\210\352\270\260 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\201\235\352\271\214\354\247\200 \354\261\205\354\236\204\354\247\200\353\212\224.svg" "b/public/images/keywords/\353\201\235\352\271\214\354\247\200 \354\261\205\354\236\204\354\247\200\353\212\224.svg"
new file mode 100644
index 00000000..9d905f50
--- /dev/null
+++ "b/public/images/keywords/\353\201\235\352\271\214\354\247\200 \354\261\205\354\236\204\354\247\200\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\202\264\353\251\264\355\231\224\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\202\264\353\251\264\355\231\224\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..b83c8c65
--- /dev/null
+++ "b/public/images/keywords/\353\202\264\353\251\264\355\231\224\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\205\274\353\246\254\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\205\274\353\246\254\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..0e388b5c
--- /dev/null
+++ "b/public/images/keywords/\353\205\274\353\246\254\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\205\274\354\235\230 \353\260\251\355\226\245\354\235\204 \354\236\241\354\225\204\354\243\274\353\212\224.svg" "b/public/images/keywords/\353\205\274\354\235\230 \353\260\251\355\226\245\354\235\204 \354\236\241\354\225\204\354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..3bc2f951
--- /dev/null
+++ "b/public/images/keywords/\353\205\274\354\235\230 \353\260\251\355\226\245\354\235\204 \354\236\241\354\225\204\354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\205\274\354\235\230\352\260\200 \354\246\220\352\261\260\354\232\264.svg" "b/public/images/keywords/\353\205\274\354\235\230\352\260\200 \354\246\220\352\261\260\354\232\264.svg"
new file mode 100644
index 00000000..ca57f12b
--- /dev/null
+++ "b/public/images/keywords/\353\205\274\354\235\230\352\260\200 \354\246\220\352\261\260\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\205\274\354\235\230\354\227\220 \354\240\201\352\267\271\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\205\274\354\235\230\354\227\220 \354\240\201\352\267\271\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..70bbeca3
--- /dev/null
+++ "b/public/images/keywords/\353\205\274\354\235\230\354\227\220 \354\240\201\352\267\271\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\205\274\354\235\230\354\235\230 \352\262\260\354\235\204 \353\247\236\354\266\224\353\212\224.svg" "b/public/images/keywords/\353\205\274\354\235\230\354\235\230 \352\262\260\354\235\204 \353\247\236\354\266\224\353\212\224.svg"
new file mode 100644
index 00000000..ee9086eb
--- /dev/null
+++ "b/public/images/keywords/\353\205\274\354\235\230\354\235\230 \352\262\260\354\235\204 \353\247\236\354\266\224\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\205\274\354\237\201 \355\232\214\355\224\274\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\205\274\354\237\201 \355\232\214\355\224\274\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..e0047511
--- /dev/null
+++ "b/public/images/keywords/\353\205\274\354\237\201 \355\232\214\355\224\274\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\205\274\354\240\220\354\235\204 \354\236\230 \354\236\241\353\212\224.svg" "b/public/images/keywords/\353\205\274\354\240\220\354\235\204 \354\236\230 \354\236\241\353\212\224.svg"
new file mode 100644
index 00000000..1e480fcb
--- /dev/null
+++ "b/public/images/keywords/\353\205\274\354\240\220\354\235\204 \354\236\230 \354\236\241\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\210\210\354\271\230\352\260\200 \353\271\240\353\245\270.svg" "b/public/images/keywords/\353\210\210\354\271\230\352\260\200 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..b7dcee42
--- /dev/null
+++ "b/public/images/keywords/\353\210\210\354\271\230\352\260\200 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\213\244\354\213\234 \355\225\250\352\273\230\355\225\230\352\263\240 \354\213\266\354\235\200.svg" "b/public/images/keywords/\353\213\244\354\213\234 \355\225\250\352\273\230\355\225\230\352\263\240 \354\213\266\354\235\200.svg"
new file mode 100644
index 00000000..fc44ec93
--- /dev/null
+++ "b/public/images/keywords/\353\213\244\354\213\234 \355\225\250\352\273\230\355\225\230\352\263\240 \354\213\266\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\213\244\354\236\254\353\213\244\353\212\245\355\225\234.svg" "b/public/images/keywords/\353\213\244\354\236\254\353\213\244\353\212\245\355\225\234.svg"
new file mode 100644
index 00000000..c63ffec3
--- /dev/null
+++ "b/public/images/keywords/\353\213\244\354\236\254\353\213\244\353\212\245\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\213\250\352\270\260 \354\225\225\353\260\225\354\227\220 \354\225\275\355\225\234.svg" "b/public/images/keywords/\353\213\250\352\270\260 \354\225\225\353\260\225\354\227\220 \354\225\275\355\225\234.svg"
new file mode 100644
index 00000000..7b879606
--- /dev/null
+++ "b/public/images/keywords/\353\213\250\352\270\260 \354\225\225\353\260\225\354\227\220 \354\225\275\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\214\200\354\225\210\354\235\204 \355\225\250\352\273\230 \354\240\234\354\213\234\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\214\200\354\225\210\354\235\204 \355\225\250\352\273\230 \354\240\234\354\213\234\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..50981ddf
--- /dev/null
+++ "b/public/images/keywords/\353\214\200\354\225\210\354\235\204 \355\225\250\352\273\230 \354\240\234\354\213\234\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\214\200\355\231\224\353\245\274 \354\235\264\353\201\204\353\212\224.svg" "b/public/images/keywords/\353\214\200\355\231\224\353\245\274 \354\235\264\353\201\204\353\212\224.svg"
new file mode 100644
index 00000000..f42348a0
--- /dev/null
+++ "b/public/images/keywords/\353\214\200\355\231\224\353\245\274 \354\235\264\353\201\204\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\215\260\354\235\264\355\204\260\354\227\220 \352\260\225\355\225\234.svg" "b/public/images/keywords/\353\215\260\354\235\264\355\204\260\354\227\220 \352\260\225\355\225\234.svg"
new file mode 100644
index 00000000..824bdcd9
--- /dev/null
+++ "b/public/images/keywords/\353\215\260\354\235\264\355\204\260\354\227\220 \352\260\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\217\204\354\240\204\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\217\204\354\240\204\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..1bb18f6c
--- /dev/null
+++ "b/public/images/keywords/\353\217\204\354\240\204\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\217\205\353\246\275\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\217\205\353\246\275\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..3700a051
--- /dev/null
+++ "b/public/images/keywords/\353\217\205\353\246\275\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\217\231\353\243\214\354\235\230 \352\260\225\354\240\220\354\235\204 \354\225\214\354\225\204\353\263\264\353\212\224.svg" "b/public/images/keywords/\353\217\231\353\243\214\354\235\230 \352\260\225\354\240\220\354\235\204 \354\225\214\354\225\204\353\263\264\353\212\224.svg"
new file mode 100644
index 00000000..d07101d9
--- /dev/null
+++ "b/public/images/keywords/\353\217\231\353\243\214\354\235\230 \352\260\225\354\240\220\354\235\204 \354\225\214\354\225\204\353\263\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\221\220\354\204\234\354\227\206\353\212\224.svg" "b/public/images/keywords/\353\221\220\354\204\234\354\227\206\353\212\224.svg"
new file mode 100644
index 00000000..0dc84079
--- /dev/null
+++ "b/public/images/keywords/\353\221\220\354\204\234\354\227\206\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\223\240\353\223\240\355\225\234.svg" "b/public/images/keywords/\353\223\240\353\223\240\355\225\234.svg"
new file mode 100644
index 00000000..aff00466
--- /dev/null
+++ "b/public/images/keywords/\353\223\240\353\223\240\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\224\224\355\205\214\354\235\274\354\227\220 \354\271\230\354\232\260\354\271\230\353\212\224.svg" "b/public/images/keywords/\353\224\224\355\205\214\354\235\274\354\227\220 \354\271\230\354\232\260\354\271\230\353\212\224.svg"
new file mode 100644
index 00000000..47e4e9a6
--- /dev/null
+++ "b/public/images/keywords/\353\224\224\355\205\214\354\235\274\354\227\220 \354\271\230\354\232\260\354\271\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\224\224\355\205\214\354\235\274\354\235\204 \353\206\223\354\271\230\354\247\200 \354\225\212\353\212\224.svg" "b/public/images/keywords/\353\224\224\355\205\214\354\235\274\354\235\204 \353\206\223\354\271\230\354\247\200 \354\225\212\353\212\224.svg"
new file mode 100644
index 00000000..08b7ade1
--- /dev/null
+++ "b/public/images/keywords/\353\224\224\355\205\214\354\235\274\354\235\204 \353\206\223\354\271\230\354\247\200 \354\225\212\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\224\260\353\234\273\355\225\234.svg" "b/public/images/keywords/\353\224\260\353\234\273\355\225\234.svg"
new file mode 100644
index 00000000..69e5c299
--- /dev/null
+++ "b/public/images/keywords/\353\224\260\353\234\273\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\246\254\353\215\224\354\213\255\354\235\264 \354\236\210\353\212\224.svg" "b/public/images/keywords/\353\246\254\353\215\224\354\213\255\354\235\264 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..185cddab
--- /dev/null
+++ "b/public/images/keywords/\353\246\254\353\215\224\354\213\255\354\235\264 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\246\254\354\206\214\354\212\244\353\245\274 \354\225\204\353\201\274\353\212\224.svg" "b/public/images/keywords/\353\246\254\354\206\214\354\212\244\353\245\274 \354\225\204\353\201\274\353\212\224.svg"
new file mode 100644
index 00000000..d93619cb
--- /dev/null
+++ "b/public/images/keywords/\353\246\254\354\206\214\354\212\244\353\245\274 \354\225\204\353\201\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\246\254\354\212\244\355\201\254 \355\232\214\355\224\274\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\246\254\354\212\244\355\201\254 \355\232\214\355\224\274\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..fb70247f
--- /dev/null
+++ "b/public/images/keywords/\353\246\254\354\212\244\355\201\254 \355\232\214\355\224\274\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\246\254\354\212\244\355\201\254\353\245\274 \353\257\270\353\246\254 \353\263\264\353\212\224.svg" "b/public/images/keywords/\353\246\254\354\212\244\355\201\254\353\245\274 \353\257\270\353\246\254 \353\263\264\353\212\224.svg"
new file mode 100644
index 00000000..cbba309d
--- /dev/null
+++ "b/public/images/keywords/\353\246\254\354\212\244\355\201\254\353\245\274 \353\257\270\353\246\254 \353\263\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\246\254\354\212\244\355\201\254\354\227\220 \353\257\274\352\260\220\355\225\234.svg" "b/public/images/keywords/\353\246\254\354\212\244\355\201\254\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
new file mode 100644
index 00000000..ceeb7489
--- /dev/null
+++ "b/public/images/keywords/\353\246\254\354\212\244\355\201\254\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\210\352\260\220 \352\270\260\354\244\200\354\235\264 \353\252\205\355\231\225\355\225\234.svg" "b/public/images/keywords/\353\247\210\352\260\220 \352\270\260\354\244\200\354\235\264 \353\252\205\355\231\225\355\225\234.svg"
new file mode 100644
index 00000000..03c4fbdb
--- /dev/null
+++ "b/public/images/keywords/\353\247\210\352\260\220 \352\270\260\354\244\200\354\235\264 \353\252\205\355\231\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\210\352\260\220 \354\247\201\354\240\204 \353\252\260\354\236\205\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\247\210\352\260\220 \354\247\201\354\240\204 \353\252\260\354\236\205\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..ffa945f9
--- /dev/null
+++ "b/public/images/keywords/\353\247\210\352\260\220 \354\247\201\354\240\204 \353\252\260\354\236\205\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\220\352\263\274 \355\226\211\353\217\231\354\235\264 \354\235\274\354\271\230\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\247\220\352\263\274 \355\226\211\353\217\231\354\235\264 \354\235\274\354\271\230\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..52fdb291
--- /dev/null
+++ "b/public/images/keywords/\353\247\220\352\263\274 \355\226\211\353\217\231\354\235\264 \354\235\274\354\271\230\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\220\353\263\264\353\213\244 \353\254\270\354\204\234\355\230\225\354\235\270.svg" "b/public/images/keywords/\353\247\220\353\263\264\353\213\244 \353\254\270\354\204\234\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..0ec0a408
--- /dev/null
+++ "b/public/images/keywords/\353\247\220\353\263\264\353\213\244 \353\254\270\354\204\234\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\220\354\227\220 \354\213\240\353\242\260\352\260\200 \354\213\244\353\246\254\353\212\224.svg" "b/public/images/keywords/\353\247\220\354\227\220 \354\213\240\353\242\260\352\260\200 \354\213\244\353\246\254\353\212\224.svg"
new file mode 100644
index 00000000..87e77f85
--- /dev/null
+++ "b/public/images/keywords/\353\247\220\354\227\220 \354\213\240\353\242\260\352\260\200 \354\213\244\353\246\254\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\220\354\227\220 \354\235\274\352\264\200\354\204\261\354\235\264 \354\236\210\353\212\224.svg" "b/public/images/keywords/\353\247\220\354\227\220 \354\235\274\352\264\200\354\204\261\354\235\264 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..9399bbca
--- /dev/null
+++ "b/public/images/keywords/\353\247\220\354\227\220 \354\235\274\352\264\200\354\204\261\354\235\264 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\220\354\235\204 \354\236\230 \355\225\230\353\212\224.svg" "b/public/images/keywords/\353\247\220\354\235\204 \354\236\230 \355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..85de0a91
--- /dev/null
+++ "b/public/images/keywords/\353\247\220\354\235\204 \354\236\230 \355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\245\353\235\275 \354\235\264\355\225\264\352\260\200 \353\271\240\353\245\270.svg" "b/public/images/keywords/\353\247\245\353\235\275 \354\235\264\355\225\264\352\260\200 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..38e06d43
--- /dev/null
+++ "b/public/images/keywords/\353\247\245\353\235\275 \354\235\264\355\225\264\352\260\200 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\247\245\353\235\275\354\235\204 \354\236\230 \354\235\264\355\225\264\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\247\245\353\235\275\354\235\204 \354\236\230 \354\235\264\355\225\264\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..64db8d7a
--- /dev/null
+++ "b/public/images/keywords/\353\247\245\353\235\275\354\235\204 \354\236\230 \354\235\264\355\225\264\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\251\230\355\203\210\354\235\264 \352\260\225\355\225\234.svg" "b/public/images/keywords/\353\251\230\355\203\210\354\235\264 \352\260\225\355\225\234.svg"
new file mode 100644
index 00000000..ac0fa618
--- /dev/null
+++ "b/public/images/keywords/\353\251\230\355\203\210\354\235\264 \352\260\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\252\251\354\240\201 \354\235\230\354\213\235\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\252\251\354\240\201 \354\235\230\354\213\235\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..e0af12d5
--- /dev/null
+++ "b/public/images/keywords/\353\252\251\354\240\201 \354\235\230\354\213\235\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\252\251\355\221\234 \354\247\200\355\226\245\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\252\251\355\221\234 \354\247\200\355\226\245\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..11d09390
--- /dev/null
+++ "b/public/images/keywords/\353\252\251\355\221\234 \354\247\200\355\226\245\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\252\251\355\221\234 \354\247\221\354\260\251\355\230\225\354\235\270.svg" "b/public/images/keywords/\353\252\251\355\221\234 \354\247\221\354\260\251\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..d9246b79
--- /dev/null
+++ "b/public/images/keywords/\353\252\251\355\221\234 \354\247\221\354\260\251\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\252\251\355\221\234\353\245\274 \352\263\265\354\234\240\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\252\251\355\221\234\353\245\274 \352\263\265\354\234\240\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..5337cf95
--- /dev/null
+++ "b/public/images/keywords/\353\252\251\355\221\234\353\245\274 \352\263\265\354\234\240\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\252\260\354\225\204\354\204\234 \355\225\230\353\212\224 \355\216\270\354\235\270.svg" "b/public/images/keywords/\353\252\260\354\225\204\354\204\234 \355\225\230\353\212\224 \355\216\270\354\235\270.svg"
new file mode 100644
index 00000000..86dcae3f
--- /dev/null
+++ "b/public/images/keywords/\353\252\260\354\225\204\354\204\234 \355\225\230\353\212\224 \355\216\270\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\252\260\354\236\205 \354\213\234 \352\270\260\353\263\265\354\235\264 \355\201\260.svg" "b/public/images/keywords/\353\252\260\354\236\205 \354\213\234 \352\270\260\353\263\265\354\235\264 \355\201\260.svg"
new file mode 100644
index 00000000..69c3bd7d
--- /dev/null
+++ "b/public/images/keywords/\353\252\260\354\236\205 \354\213\234 \352\270\260\353\263\265\354\235\264 \355\201\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\254\265\353\254\265\355\236\210 \355\225\264\353\202\264\353\212\224.svg" "b/public/images/keywords/\353\254\265\353\254\265\355\236\210 \355\225\264\353\202\264\353\212\224.svg"
new file mode 100644
index 00000000..cc87fe87
--- /dev/null
+++ "b/public/images/keywords/\353\254\265\353\254\265\355\236\210 \355\225\264\353\202\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\254\270\354\240\234 \355\225\264\352\262\260\353\240\245\354\235\264 \353\233\260\354\226\264\353\202\234.svg" "b/public/images/keywords/\353\254\270\354\240\234 \355\225\264\352\262\260\353\240\245\354\235\264 \353\233\260\354\226\264\353\202\234.svg"
new file mode 100644
index 00000000..7c281c2e
--- /dev/null
+++ "b/public/images/keywords/\353\254\270\354\240\234 \355\225\264\352\262\260\353\240\245\354\235\264 \353\233\260\354\226\264\353\202\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\260\251\355\226\245 \354\240\204\355\231\230\354\235\264 \354\236\246\354\235\200.svg" "b/public/images/keywords/\353\260\251\355\226\245 \354\240\204\355\231\230\354\235\264 \354\236\246\354\235\200.svg"
new file mode 100644
index 00000000..51bb459c
--- /dev/null
+++ "b/public/images/keywords/\353\260\251\355\226\245 \354\240\204\355\231\230\354\235\264 \354\236\246\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\260\251\355\226\245 \354\240\234\354\213\234\354\227\220 \353\212\245\355\225\234.svg" "b/public/images/keywords/\353\260\251\355\226\245 \354\240\234\354\213\234\354\227\220 \353\212\245\355\225\234.svg"
new file mode 100644
index 00000000..8d7108a5
--- /dev/null
+++ "b/public/images/keywords/\353\260\251\355\226\245 \354\240\234\354\213\234\354\227\220 \353\212\245\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\260\251\355\226\245\354\204\261\354\235\264 \353\260\224\353\200\214\353\212\224.svg" "b/public/images/keywords/\353\260\251\355\226\245\354\204\261\354\235\264 \353\260\224\353\200\214\353\212\224.svg"
new file mode 100644
index 00000000..a9bd6264
--- /dev/null
+++ "b/public/images/keywords/\353\260\251\355\226\245\354\204\261\354\235\264 \353\260\224\353\200\214\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\260\251\355\226\245\354\235\204 \354\234\240\354\247\200\355\225\234.svg" "b/public/images/keywords/\353\260\251\355\226\245\354\235\204 \354\234\240\354\247\200\355\225\234.svg"
new file mode 100644
index 00000000..fd77e83e
--- /dev/null
+++ "b/public/images/keywords/\353\260\251\355\226\245\354\235\204 \354\234\240\354\247\200\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\260\251\355\226\245\354\235\264 \355\235\224\353\223\244\353\246\254\353\212\224.svg" "b/public/images/keywords/\353\260\251\355\226\245\354\235\264 \355\235\224\353\223\244\353\246\254\353\212\224.svg"
new file mode 100644
index 00000000..53df32d8
--- /dev/null
+++ "b/public/images/keywords/\353\260\251\355\226\245\354\235\264 \355\235\224\353\223\244\353\246\254\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\260\260\353\240\244\354\213\254 \352\271\212\354\235\200.svg" "b/public/images/keywords/\353\260\260\353\240\244\354\213\254 \352\271\212\354\235\200.svg"
new file mode 100644
index 00000000..c3b6d989
--- /dev/null
+++ "b/public/images/keywords/\353\260\260\353\240\244\354\213\254 \352\271\212\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\262\204\355\214\200\353\252\251\354\235\264 \353\220\230\353\212\224.svg" "b/public/images/keywords/\353\262\204\355\214\200\353\252\251\354\235\264 \353\220\230\353\212\224.svg"
new file mode 100644
index 00000000..2ffaed90
--- /dev/null
+++ "b/public/images/keywords/\353\262\204\355\214\200\353\252\251\354\235\264 \353\220\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\200\354\210\230\353\245\274 \352\263\240\353\240\244\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\263\200\354\210\230\353\245\274 \352\263\240\353\240\244\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..f7f430cc
--- /dev/null
+++ "b/public/images/keywords/\353\263\200\354\210\230\353\245\274 \352\263\240\353\240\244\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\200\355\231\224 \354\210\230\354\232\251\354\227\220 \354\213\234\352\260\204\354\235\264 \355\225\204\354\232\224\355\225\234.svg" "b/public/images/keywords/\353\263\200\355\231\224 \354\210\230\354\232\251\354\227\220 \354\213\234\352\260\204\354\235\264 \355\225\204\354\232\224\355\225\234.svg"
new file mode 100644
index 00000000..177469fa
--- /dev/null
+++ "b/public/images/keywords/\353\263\200\355\231\224 \354\210\230\354\232\251\354\227\220 \354\213\234\352\260\204\354\235\264 \355\225\204\354\232\224\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\200\355\231\224\354\227\220 \354\230\210\353\257\274\355\225\234.svg" "b/public/images/keywords/\353\263\200\355\231\224\354\227\220 \354\230\210\353\257\274\355\225\234.svg"
new file mode 100644
index 00000000..7edb5fad
--- /dev/null
+++ "b/public/images/keywords/\353\263\200\355\231\224\354\227\220 \354\230\210\353\257\274\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\221\353\252\251\354\235\204 \354\244\204\354\235\264\353\212\224.svg" "b/public/images/keywords/\353\263\221\353\252\251\354\235\204 \354\244\204\354\235\264\353\212\224.svg"
new file mode 100644
index 00000000..ffc6303d
--- /dev/null
+++ "b/public/images/keywords/\353\263\221\353\252\251\354\235\204 \354\244\204\354\235\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\221\353\252\251\354\235\204 \355\225\264\352\262\260\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\263\221\353\252\251\354\235\204 \355\225\264\352\262\260\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..bdac345a
--- /dev/null
+++ "b/public/images/keywords/\353\263\221\353\252\251\354\235\204 \355\225\264\352\262\260\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\264\354\231\204\354\235\204 \354\247\200\355\226\245\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\263\264\354\231\204\354\235\204 \354\247\200\355\226\245\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..56908962
--- /dev/null
+++ "b/public/images/keywords/\353\263\264\354\231\204\354\235\204 \354\247\200\355\226\245\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\264\354\235\264\354\247\200 \354\225\212\353\212\224 \354\235\274\354\235\204 \354\261\231\352\270\260\353\212\224.svg" "b/public/images/keywords/\353\263\264\354\235\264\354\247\200 \354\225\212\353\212\224 \354\235\274\354\235\204 \354\261\231\352\270\260\353\212\224.svg"
new file mode 100644
index 00000000..e3a1d97d
--- /dev/null
+++ "b/public/images/keywords/\353\263\264\354\235\264\354\247\200 \354\225\212\353\212\224 \354\235\274\354\235\204 \354\261\231\352\270\260\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\265\354\236\241\355\225\234 \352\261\270 \353\213\250\354\210\234\355\231\224\355\225\230\353\212\224.svg" "b/public/images/keywords/\353\263\265\354\236\241\355\225\234 \352\261\270 \353\213\250\354\210\234\355\231\224\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..86996eac
--- /dev/null
+++ "b/public/images/keywords/\353\263\265\354\236\241\355\225\234 \352\261\270 \353\213\250\354\210\234\355\231\224\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\263\270\354\247\210 \354\247\200\355\226\245\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\263\270\354\247\210 \354\247\200\355\226\245\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..d1ea086d
--- /dev/null
+++ "b/public/images/keywords/\353\263\270\354\247\210 \354\247\200\355\226\245\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\266\200\353\213\264 \354\227\206\354\235\264 \354\235\230\352\262\254\354\235\204 \353\254\273\353\212\224.svg" "b/public/images/keywords/\353\266\200\353\213\264 \354\227\206\354\235\264 \354\235\230\352\262\254\354\235\204 \353\254\273\353\212\224.svg"
new file mode 100644
index 00000000..5ac18664
--- /dev/null
+++ "b/public/images/keywords/\353\266\200\353\213\264 \354\227\206\354\235\264 \354\235\230\352\262\254\354\235\204 \353\254\273\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\266\200\353\213\264\354\235\204 \354\214\223\354\225\204\353\221\220\353\212\224.svg" "b/public/images/keywords/\353\266\200\353\213\264\354\235\204 \354\214\223\354\225\204\353\221\220\353\212\224.svg"
new file mode 100644
index 00000000..1cdd07f6
--- /dev/null
+++ "b/public/images/keywords/\353\266\200\353\213\264\354\235\204 \354\214\223\354\225\204\353\221\220\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\266\204\354\204\235\354\240\201\354\235\270.svg" "b/public/images/keywords/\353\266\204\354\204\235\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..1838ea11
--- /dev/null
+++ "b/public/images/keywords/\353\266\204\354\204\235\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\353\266\204\354\234\204\352\270\260 \353\251\224\354\235\264\354\273\244\355\230\225.svg" "b/public/images/keywords/\353\266\204\354\234\204\352\270\260 \353\251\224\354\235\264\354\273\244\355\230\225.svg"
new file mode 100644
index 00000000..4a4bf03a
--- /dev/null
+++ "b/public/images/keywords/\353\266\204\354\234\204\352\270\260 \353\251\224\354\235\264\354\273\244\355\230\225.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\203\201\353\214\200\353\245\274 \352\263\240\353\240\244\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\203\201\353\214\200\353\245\274 \352\263\240\353\240\244\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..c3b2f251
--- /dev/null
+++ "b/public/images/keywords/\354\203\201\353\214\200\353\245\274 \352\263\240\353\240\244\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\203\201\355\231\251 \354\235\230\354\241\264\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\203\201\355\231\251 \354\235\230\354\241\264\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..bbc0cb2e
--- /dev/null
+++ "b/public/images/keywords/\354\203\201\355\231\251 \354\235\230\354\241\264\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\203\201\355\231\251\353\263\204 \353\214\200\354\235\221\354\235\264 \354\234\240\354\227\260\355\225\234.svg" "b/public/images/keywords/\354\203\201\355\231\251\353\263\204 \353\214\200\354\235\221\354\235\264 \354\234\240\354\227\260\355\225\234.svg"
new file mode 100644
index 00000000..032f57f7
--- /dev/null
+++ "b/public/images/keywords/\354\203\201\355\231\251\353\263\204 \353\214\200\354\235\221\354\235\264 \354\234\240\354\227\260\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\203\201\355\231\251\354\227\220 \353\247\236\352\262\214 \354\206\214\355\206\265\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\203\201\355\231\251\354\227\220 \353\247\236\352\262\214 \354\206\214\355\206\265\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..da76dde8
--- /dev/null
+++ "b/public/images/keywords/\354\203\201\355\231\251\354\227\220 \353\247\236\352\262\214 \354\206\214\355\206\265\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\203\235\352\260\201\354\235\264 \353\247\216\354\235\200.svg" "b/public/images/keywords/\354\203\235\352\260\201\354\235\264 \353\247\216\354\235\200.svg"
new file mode 100644
index 00000000..683f2331
--- /dev/null
+++ "b/public/images/keywords/\354\203\235\352\260\201\354\235\264 \353\247\216\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\240\355\203\235\354\235\204 \352\263\240\353\257\274\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\204\240\355\203\235\354\235\204 \352\263\240\353\257\274\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..6878c3c6
--- /dev/null
+++ "b/public/images/keywords/\354\204\240\355\203\235\354\235\204 \352\263\240\353\257\274\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\244\353\223\235\353\240\245\354\235\264 \353\206\222\354\235\200.svg" "b/public/images/keywords/\354\204\244\353\223\235\353\240\245\354\235\264 \353\206\222\354\235\200.svg"
new file mode 100644
index 00000000..4ca51a0e
--- /dev/null
+++ "b/public/images/keywords/\354\204\244\353\223\235\353\240\245\354\235\264 \353\206\222\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\244\353\223\235\353\263\264\353\213\244 \354\204\244\353\252\205\355\230\225\354\235\270.svg" "b/public/images/keywords/\354\204\244\353\223\235\353\263\264\353\213\244 \354\204\244\353\252\205\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..7c93b30d
--- /dev/null
+++ "b/public/images/keywords/\354\204\244\353\223\235\353\263\264\353\213\244 \354\204\244\353\252\205\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\261\352\263\274 \354\244\221\354\213\254\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\204\261\352\263\274 \354\244\221\354\213\254\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..190ee18d
--- /dev/null
+++ "b/public/images/keywords/\354\204\261\352\263\274 \354\244\221\354\213\254\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\261\354\236\245 \353\260\251\355\226\245\354\235\264 \353\266\204\353\252\205\355\225\234.svg" "b/public/images/keywords/\354\204\261\354\236\245 \353\260\251\355\226\245\354\235\264 \353\266\204\353\252\205\355\225\234.svg"
new file mode 100644
index 00000000..2a0bf988
--- /dev/null
+++ "b/public/images/keywords/\354\204\261\354\236\245 \353\260\251\355\226\245\354\235\264 \353\266\204\353\252\205\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\261\354\236\245\354\235\264 \354\225\210\354\240\225\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\204\261\354\236\245\354\235\264 \354\225\210\354\240\225\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..751c2d08
--- /dev/null
+++ "b/public/images/keywords/\354\204\261\354\236\245\354\235\264 \354\225\210\354\240\225\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\261\354\267\250\354\232\225\354\235\264 \353\206\222\354\235\200.svg" "b/public/images/keywords/\354\204\261\354\267\250\354\232\225\354\235\264 \353\206\222\354\235\200.svg"
new file mode 100644
index 00000000..a34bb3f8
--- /dev/null
+++ "b/public/images/keywords/\354\204\261\354\267\250\354\232\225\354\235\264 \353\206\222\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\270\354\213\254\355\225\234.svg" "b/public/images/keywords/\354\204\270\354\213\254\355\225\234.svg"
new file mode 100644
index 00000000..b360b309
--- /dev/null
+++ "b/public/images/keywords/\354\204\270\354\213\254\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\204\274\354\212\244 \354\236\210\353\212\224.svg" "b/public/images/keywords/\354\204\274\354\212\244 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..07fd1115
--- /dev/null
+++ "b/public/images/keywords/\354\204\274\354\212\244 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\206\214\355\206\265\354\235\264 \354\206\214\352\267\271\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\206\214\355\206\265\354\235\264 \354\206\214\352\267\271\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..61f06ec2
--- /dev/null
+++ "b/public/images/keywords/\354\206\214\355\206\265\354\235\264 \354\206\214\352\267\271\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\206\215\353\217\204 \355\216\270\354\260\250\352\260\200 \355\201\260.svg" "b/public/images/keywords/\354\206\215\353\217\204 \355\216\270\354\260\250\352\260\200 \355\201\260.svg"
new file mode 100644
index 00000000..07fc2eaa
--- /dev/null
+++ "b/public/images/keywords/\354\206\215\353\217\204 \355\216\270\354\260\250\352\260\200 \355\201\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\206\224\354\204\240\354\210\230\353\262\224\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\206\224\354\204\240\354\210\230\353\262\224\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..0c5bc852
--- /dev/null
+++ "b/public/images/keywords/\354\206\224\354\204\240\354\210\230\353\262\224\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\206\224\354\247\201\355\225\234.svg" "b/public/images/keywords/\354\206\224\354\247\201\355\225\234.svg"
new file mode 100644
index 00000000..caa2120b
--- /dev/null
+++ "b/public/images/keywords/\354\206\224\354\247\201\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\210\230\354\232\251\353\240\245\354\235\264 \354\242\213\354\235\200.svg" "b/public/images/keywords/\354\210\230\354\232\251\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
new file mode 100644
index 00000000..867dc738
--- /dev/null
+++ "b/public/images/keywords/\354\210\230\354\232\251\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\210\230\354\232\251\354\235\264 \353\212\220\353\246\260.svg" "b/public/images/keywords/\354\210\230\354\232\251\354\235\264 \353\212\220\353\246\260.svg"
new file mode 100644
index 00000000..a1cffb5e
--- /dev/null
+++ "b/public/images/keywords/\354\210\230\354\232\251\354\235\264 \353\212\220\353\246\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\212\244\354\212\244\353\241\234 \353\217\231\352\270\260\353\266\200\354\227\254\352\260\200 \353\220\230\353\212\224.svg" "b/public/images/keywords/\354\212\244\354\212\244\353\241\234 \353\217\231\352\270\260\353\266\200\354\227\254\352\260\200 \353\220\230\353\212\224.svg"
new file mode 100644
index 00000000..4eadc0fe
--- /dev/null
+++ "b/public/images/keywords/\354\212\244\354\212\244\353\241\234 \353\217\231\352\270\260\353\266\200\354\227\254\352\260\200 \353\220\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\212\244\354\212\244\353\241\234 \354\227\204\352\262\251\355\225\234.svg" "b/public/images/keywords/\354\212\244\354\212\244\353\241\234 \354\227\204\352\262\251\355\225\234.svg"
new file mode 100644
index 00000000..564ded85
--- /dev/null
+++ "b/public/images/keywords/\354\212\244\354\212\244\353\241\234 \354\227\204\352\262\251\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\212\244\354\212\244\353\241\234 \354\240\220\352\262\200\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\212\244\354\212\244\353\241\234 \354\240\220\352\262\200\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..87716401
--- /dev/null
+++ "b/public/images/keywords/\354\212\244\354\212\244\353\241\234 \354\240\220\352\262\200\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\212\244\355\212\270\353\240\210\354\212\244 \353\210\204\354\240\201\355\230\225\354\235\270.svg" "b/public/images/keywords/\354\212\244\355\212\270\353\240\210\354\212\244 \353\210\204\354\240\201\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..105a0d7e
--- /dev/null
+++ "b/public/images/keywords/\354\212\244\355\212\270\353\240\210\354\212\244 \353\210\204\354\240\201\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\234\355\226\211\354\260\251\354\230\244\353\245\274 \354\244\204\354\235\264\353\212\224.svg" "b/public/images/keywords/\354\213\234\355\226\211\354\260\251\354\230\244\353\245\274 \354\244\204\354\235\264\353\212\224.svg"
new file mode 100644
index 00000000..a8f5319a
--- /dev/null
+++ "b/public/images/keywords/\354\213\234\355\226\211\354\260\251\354\230\244\353\245\274 \354\244\204\354\235\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\240\353\242\260\352\260\220 \354\236\210\353\212\224.svg" "b/public/images/keywords/\354\213\240\353\242\260\352\260\220 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..1c7aa569
--- /dev/null
+++ "b/public/images/keywords/\354\213\240\353\242\260\352\260\220 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\214\223\354\225\204\352\260\200\353\212\224.svg" "b/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\214\223\354\225\204\352\260\200\353\212\224.svg"
new file mode 100644
index 00000000..3b7bb741
--- /dev/null
+++ "b/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\214\223\354\225\204\352\260\200\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\214\223\354\225\204\354\230\250.svg" "b/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\214\223\354\225\204\354\230\250.svg"
new file mode 100644
index 00000000..94bd6b1c
--- /dev/null
+++ "b/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\214\223\354\225\204\354\230\250.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\243\274\353\212\224.svg" "b/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..9188df2d
--- /dev/null
+++ "b/public/images/keywords/\354\213\240\353\242\260\353\245\274 \354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\240\353\242\260\354\235\230 \352\270\260\354\244\200\354\235\264 \353\220\230\353\212\224.svg" "b/public/images/keywords/\354\213\240\353\242\260\354\235\230 \352\270\260\354\244\200\354\235\264 \353\220\230\353\212\224.svg"
new file mode 100644
index 00000000..ac0d78a3
--- /dev/null
+++ "b/public/images/keywords/\354\213\240\353\242\260\354\235\230 \352\270\260\354\244\200\354\235\264 \353\220\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\240\354\244\221\355\225\230\352\262\214 \355\214\220\353\213\250\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\213\240\354\244\221\355\225\230\352\262\214 \355\214\220\353\213\250\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..221d1754
--- /dev/null
+++ "b/public/images/keywords/\354\213\240\354\244\221\355\225\230\352\262\214 \355\214\220\353\213\250\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\353\254\264 \352\260\220\352\260\201\354\235\264 \353\271\240\353\245\270.svg" "b/public/images/keywords/\354\213\244\353\254\264 \352\260\220\352\260\201\354\235\264 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..f9c7ddfb
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\353\254\264 \352\260\220\352\260\201\354\235\264 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\353\254\264\354\231\200 \352\270\260\355\232\215\354\235\204 \354\236\207\353\212\224.svg" "b/public/images/keywords/\354\213\244\353\254\264\354\231\200 \352\270\260\355\232\215\354\235\204 \354\236\207\353\212\224.svg"
new file mode 100644
index 00000000..7d949169
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\353\254\264\354\231\200 \352\270\260\355\232\215\354\235\204 \354\236\207\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\354\210\230\353\241\234\353\266\200\355\204\260 \354\204\261\354\236\245\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\213\244\354\210\230\353\241\234\353\266\200\355\204\260 \354\204\261\354\236\245\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..2850e6cf
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\354\210\230\353\241\234\353\266\200\355\204\260 \354\204\261\354\236\245\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\354\210\230\353\245\274 \352\270\260\355\232\214\353\241\234 \353\260\224\352\276\270\353\212\224.svg" "b/public/images/keywords/\354\213\244\354\210\230\353\245\274 \352\270\260\355\232\214\353\241\234 \353\260\224\352\276\270\353\212\224.svg"
new file mode 100644
index 00000000..02c4fccf
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\354\210\230\353\245\274 \352\270\260\355\232\214\353\241\234 \353\260\224\352\276\270\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\355\226\211 \353\213\250\352\263\204\353\245\274 \353\252\205\355\231\225\355\236\210 \355\225\230\353\212\224.svg" "b/public/images/keywords/\354\213\244\355\226\211 \353\213\250\352\263\204\353\245\274 \353\252\205\355\231\225\355\236\210 \355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..bbbcdf4d
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\355\226\211 \353\213\250\352\263\204\353\245\274 \353\252\205\355\231\225\355\236\210 \355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\355\226\211\352\271\214\354\247\200 \354\227\260\352\262\260\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\213\244\355\226\211\352\271\214\354\247\200 \354\227\260\352\262\260\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..e56ee9d5
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\355\226\211\352\271\214\354\247\200 \354\227\260\352\262\260\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\355\226\211\354\234\274\353\241\234 \354\230\256\352\270\264.svg" "b/public/images/keywords/\354\213\244\355\226\211\354\234\274\353\241\234 \354\230\256\352\270\264.svg"
new file mode 100644
index 00000000..561544dc
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\355\226\211\354\234\274\353\241\234 \354\230\256\352\270\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\213\244\355\226\211\354\235\264 \353\212\246\354\226\264\354\247\200\353\212\224.svg" "b/public/images/keywords/\354\213\244\355\226\211\354\235\264 \353\212\246\354\226\264\354\247\200\353\212\224.svg"
new file mode 100644
index 00000000..8124492f
--- /dev/null
+++ "b/public/images/keywords/\354\213\244\355\226\211\354\235\264 \353\212\246\354\226\264\354\247\200\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\225\204\354\235\264\353\224\224\354\226\264\352\260\200 \355\222\215\353\266\200\355\225\234.svg" "b/public/images/keywords/\354\225\204\354\235\264\353\224\224\354\226\264\352\260\200 \355\222\215\353\266\200\355\225\234.svg"
new file mode 100644
index 00000000..6f7a9764
--- /dev/null
+++ "b/public/images/keywords/\354\225\204\354\235\264\353\224\224\354\226\264\352\260\200 \355\222\215\353\266\200\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\225\204\354\235\264\353\224\224\354\226\264\353\245\274 \352\265\254\354\262\264\355\231\224\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\225\204\354\235\264\353\224\224\354\226\264\353\245\274 \352\265\254\354\262\264\355\231\224\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..d22770b2
--- /dev/null
+++ "b/public/images/keywords/\354\225\204\354\235\264\353\224\224\354\226\264\353\245\274 \352\265\254\354\262\264\355\231\224\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\225\210\354\240\225\354\204\261\354\235\264 \353\202\256\354\235\200.svg" "b/public/images/keywords/\354\225\210\354\240\225\354\204\261\354\235\264 \353\202\256\354\235\200.svg"
new file mode 100644
index 00000000..62da9239
--- /dev/null
+++ "b/public/images/keywords/\354\225\210\354\240\225\354\204\261\354\235\264 \353\202\256\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\225\210\354\240\225\354\227\220 \353\250\270\353\254\264\353\245\264\353\212\224.svg" "b/public/images/keywords/\354\225\210\354\240\225\354\227\220 \353\250\270\353\254\264\353\245\264\353\212\224.svg"
new file mode 100644
index 00000000..01c85194
--- /dev/null
+++ "b/public/images/keywords/\354\225\210\354\240\225\354\227\220 \353\250\270\353\254\264\353\245\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\225\210\354\240\225\354\240\201\354\235\270 \354\204\240\355\203\235\354\235\204 \355\225\230\353\212\224.svg" "b/public/images/keywords/\354\225\210\354\240\225\354\240\201\354\235\270 \354\204\240\355\203\235\354\235\204 \355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..5c6c82a9
--- /dev/null
+++ "b/public/images/keywords/\354\225\210\354\240\225\354\240\201\354\235\270 \354\204\240\355\203\235\354\235\204 \355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\225\225\353\260\225\354\227\220 \352\260\225\355\225\264\354\247\200\353\212\224.svg" "b/public/images/keywords/\354\225\225\353\260\225\354\227\220 \352\260\225\355\225\264\354\247\200\353\212\224.svg"
new file mode 100644
index 00000000..890fecd8
--- /dev/null
+++ "b/public/images/keywords/\354\225\225\353\260\225\354\227\220 \352\260\225\355\225\264\354\247\200\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\225\275\354\206\215\354\235\204 \354\236\230 \354\247\200\355\202\244\353\212\224.svg" "b/public/images/keywords/\354\225\275\354\206\215\354\235\204 \354\236\230 \354\247\200\355\202\244\353\212\224.svg"
new file mode 100644
index 00000000..09465e03
--- /dev/null
+++ "b/public/images/keywords/\354\225\275\354\206\215\354\235\204 \354\236\230 \354\247\200\355\202\244\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\227\220\353\204\210\354\247\200 \353\204\230\354\271\230\353\212\224.svg" "b/public/images/keywords/\354\227\220\353\204\210\354\247\200 \353\204\230\354\271\230\353\212\224.svg"
new file mode 100644
index 00000000..f613b5e5
--- /dev/null
+++ "b/public/images/keywords/\354\227\220\353\204\210\354\247\200 \353\204\230\354\271\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\227\220\353\204\210\354\247\200 \355\216\270\354\260\250\352\260\200 \354\236\210\353\212\224.svg" "b/public/images/keywords/\354\227\220\353\204\210\354\247\200 \355\216\270\354\260\250\352\260\200 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..cbe8d9cc
--- /dev/null
+++ "b/public/images/keywords/\354\227\220\353\204\210\354\247\200 \355\216\270\354\260\250\352\260\200 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\227\255\355\225\240 \353\266\204\353\213\264\354\235\204 \353\252\205\355\231\225\355\236\210 \355\225\230\353\212\224.svg" "b/public/images/keywords/\354\227\255\355\225\240 \353\266\204\353\213\264\354\235\204 \353\252\205\355\231\225\355\236\210 \355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..40c9b8fa
--- /dev/null
+++ "b/public/images/keywords/\354\227\255\355\225\240 \353\266\204\353\213\264\354\235\204 \353\252\205\355\231\225\355\236\210 \355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\227\255\355\225\240\354\227\220 \354\265\234\354\204\240\354\235\204 \353\213\244\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\227\255\355\225\240\354\227\220 \354\265\234\354\204\240\354\235\204 \353\213\244\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..28fc4143
--- /dev/null
+++ "b/public/images/keywords/\354\227\255\355\225\240\354\227\220 \354\265\234\354\204\240\354\235\204 \353\213\244\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\227\260\353\235\275\354\235\264 \353\271\240\353\245\270.svg" "b/public/images/keywords/\354\227\260\353\235\275\354\235\264 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..d5158461
--- /dev/null
+++ "b/public/images/keywords/\354\227\260\353\235\275\354\235\264 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\227\264\354\240\225\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\227\264\354\240\225\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..e637549d
--- /dev/null
+++ "b/public/images/keywords/\354\227\264\354\240\225\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\230\210\353\257\274\355\225\234.svg" "b/public/images/keywords/\354\230\210\353\257\274\355\225\234.svg"
new file mode 100644
index 00000000..4974a9f2
--- /dev/null
+++ "b/public/images/keywords/\354\230\210\353\257\274\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\230\244\355\225\264\353\245\274 \354\244\204\354\235\264\353\212\224.svg" "b/public/images/keywords/\354\230\244\355\225\264\353\245\274 \354\244\204\354\235\264\353\212\224.svg"
new file mode 100644
index 00000000..0aa3b0d9
--- /dev/null
+++ "b/public/images/keywords/\354\230\244\355\225\264\353\245\274 \354\244\204\354\235\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\231\204\353\262\275\354\243\274\354\235\230\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\231\204\353\262\275\354\243\274\354\235\230\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..6f4cbc30
--- /dev/null
+++ "b/public/images/keywords/\354\231\204\353\262\275\354\243\274\354\235\230\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\231\204\354\204\261 \354\232\225\352\265\254\352\260\200 \355\201\260.svg" "b/public/images/keywords/\354\231\204\354\204\261 \354\232\225\352\265\254\352\260\200 \355\201\260.svg"
new file mode 100644
index 00000000..415c5076
--- /dev/null
+++ "b/public/images/keywords/\354\231\204\354\204\261 \354\232\225\352\265\254\352\260\200 \355\201\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\231\204\354\204\261\353\217\204 \354\244\221\354\213\234\355\230\225\354\235\270.svg" "b/public/images/keywords/\354\231\204\354\204\261\353\217\204 \354\244\221\354\213\234\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..7a5f5d60
--- /dev/null
+++ "b/public/images/keywords/\354\231\204\354\204\261\353\217\204 \354\244\221\354\213\234\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\231\204\354\204\261\353\217\204\353\245\274 \354\244\221\354\213\234\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\231\204\354\204\261\353\217\204\353\245\274 \354\244\221\354\213\234\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..8c48b4d3
--- /dev/null
+++ "b/public/images/keywords/\354\231\204\354\204\261\353\217\204\353\245\274 \354\244\221\354\213\234\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\231\270\353\266\200 \354\236\220\352\267\271\354\227\220 \353\257\274\352\260\220\355\225\234.svg" "b/public/images/keywords/\354\231\270\353\266\200 \354\236\220\352\267\271\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
new file mode 100644
index 00000000..cb965023
--- /dev/null
+++ "b/public/images/keywords/\354\231\270\353\266\200 \354\236\220\352\267\271\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\232\224\352\265\254\354\202\254\355\225\255\354\235\204 \354\236\230 \354\240\225\353\246\254\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\232\224\352\265\254\354\202\254\355\225\255\354\235\204 \354\236\230 \354\240\225\353\246\254\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..dd3b3140
--- /dev/null
+++ "b/public/images/keywords/\354\232\224\352\265\254\354\202\254\355\225\255\354\235\204 \354\236\230 \354\240\225\353\246\254\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\352\260\200 \353\263\200\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\352\260\200 \353\263\200\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..5daaebee
--- /dev/null
+++ "b/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\352\260\200 \353\263\200\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\353\245\274 \354\236\230 \354\204\270\354\232\260\353\212\224.svg" "b/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\353\245\274 \354\236\230 \354\204\270\354\232\260\353\212\224.svg"
new file mode 100644
index 00000000..ec7b54d0
--- /dev/null
+++ "b/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\353\245\274 \354\236\230 \354\204\270\354\232\260\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\353\245\274 \354\236\241\354\235\200.svg" "b/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\353\245\274 \354\236\241\354\235\200.svg"
new file mode 100644
index 00000000..210e041c
--- /dev/null
+++ "b/public/images/keywords/\354\232\260\354\204\240\354\210\234\354\234\204\353\245\274 \354\236\241\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\232\260\354\234\240\353\266\200\353\213\250\355\225\234.svg" "b/public/images/keywords/\354\232\260\354\234\240\353\266\200\353\213\250\355\225\234.svg"
new file mode 100644
index 00000000..989638f4
--- /dev/null
+++ "b/public/images/keywords/\354\232\260\354\234\240\353\266\200\353\213\250\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\234\204\352\270\260 \353\214\200\354\262\230\354\227\220 \352\260\225\355\225\234.svg" "b/public/images/keywords/\354\234\204\352\270\260 \353\214\200\354\262\230\354\227\220 \352\260\225\355\225\234.svg"
new file mode 100644
index 00000000..ffb32e97
--- /dev/null
+++ "b/public/images/keywords/\354\234\204\352\270\260 \353\214\200\354\262\230\354\227\220 \352\260\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\234\240\353\250\270\353\237\254\354\212\244\355\225\234.svg" "b/public/images/keywords/\354\234\240\353\250\270\353\237\254\354\212\244\355\225\234.svg"
new file mode 100644
index 00000000..ee196acc
--- /dev/null
+++ "b/public/images/keywords/\354\234\240\353\250\270\353\237\254\354\212\244\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\234\240\353\263\264\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\234\240\353\263\264\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..2b1dbea0
--- /dev/null
+++ "b/public/images/keywords/\354\234\240\353\263\264\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\234\240\354\227\260\354\204\261\354\235\264 \354\225\204\354\211\254\354\232\264.svg" "b/public/images/keywords/\354\234\240\354\227\260\354\204\261\354\235\264 \354\225\204\354\211\254\354\232\264.svg"
new file mode 100644
index 00000000..bc71ae06
--- /dev/null
+++ "b/public/images/keywords/\354\234\240\354\227\260\354\204\261\354\235\264 \354\225\204\354\211\254\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\234\240\354\227\260\355\225\230\352\262\214 \354\202\254\352\263\240\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\234\240\354\227\260\355\225\230\352\262\214 \354\202\254\352\263\240\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..7a21d9a9
--- /dev/null
+++ "b/public/images/keywords/\354\234\240\354\227\260\355\225\230\352\262\214 \354\202\254\352\263\240\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\221\353\213\265\354\235\264 \353\271\240\353\245\270.svg" "b/public/images/keywords/\354\235\221\353\213\265\354\235\264 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..8944126e
--- /dev/null
+++ "b/public/images/keywords/\354\235\221\353\213\265\354\235\264 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\230\352\262\254 \354\210\230\354\232\251\354\235\264 \353\212\220\353\246\260.svg" "b/public/images/keywords/\354\235\230\352\262\254 \354\210\230\354\232\251\354\235\264 \353\212\220\353\246\260.svg"
new file mode 100644
index 00000000..040f093a
--- /dev/null
+++ "b/public/images/keywords/\354\235\230\352\262\254 \354\210\230\354\232\251\354\235\264 \353\212\220\353\246\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\230\352\262\254 \355\221\234\355\230\204\354\235\264 \352\260\225\355\225\234.svg" "b/public/images/keywords/\354\235\230\352\262\254 \355\221\234\355\230\204\354\235\264 \352\260\225\355\225\234.svg"
new file mode 100644
index 00000000..932ca5b6
--- /dev/null
+++ "b/public/images/keywords/\354\235\230\352\262\254 \355\221\234\355\230\204\354\235\264 \352\260\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\230\352\262\254 \355\221\234\355\230\204\354\235\264 \354\240\201\354\235\200.svg" "b/public/images/keywords/\354\235\230\352\262\254 \355\221\234\355\230\204\354\235\264 \354\240\201\354\235\200.svg"
new file mode 100644
index 00000000..03de0720
--- /dev/null
+++ "b/public/images/keywords/\354\235\230\352\262\254 \355\221\234\355\230\204\354\235\264 \354\240\201\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\230\352\262\254\354\235\204 \354\241\264\354\244\221\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\235\230\352\262\254\354\235\204 \354\241\264\354\244\221\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..e281ff79
--- /dev/null
+++ "b/public/images/keywords/\354\235\230\352\262\254\354\235\204 \354\241\264\354\244\221\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\230\353\257\270 \354\244\221\354\213\234\355\230\225\354\235\270.svg" "b/public/images/keywords/\354\235\230\353\257\270 \354\244\221\354\213\234\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..bf62aa31
--- /dev/null
+++ "b/public/images/keywords/\354\235\230\353\257\270 \354\244\221\354\213\234\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\230\354\232\225\354\235\264 \354\225\236\354\204\234\353\212\224.svg" "b/public/images/keywords/\354\235\230\354\232\225\354\235\264 \354\225\236\354\204\234\353\212\224.svg"
new file mode 100644
index 00000000..d65eb31b
--- /dev/null
+++ "b/public/images/keywords/\354\235\230\354\232\225\354\235\264 \354\225\236\354\204\234\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\264\354\203\201 \354\213\244\355\230\204\355\230\225\354\235\270.svg" "b/public/images/keywords/\354\235\264\354\203\201 \354\213\244\355\230\204\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..f86b0412
--- /dev/null
+++ "b/public/images/keywords/\354\235\264\354\203\201 \354\213\244\355\230\204\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\264\354\203\201\354\235\204 \354\242\207\353\212\224.svg" "b/public/images/keywords/\354\235\264\354\203\201\354\235\204 \354\242\207\353\212\224.svg"
new file mode 100644
index 00000000..351d2193
--- /dev/null
+++ "b/public/images/keywords/\354\235\264\354\203\201\354\235\204 \354\242\207\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\264\354\203\201\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\235\264\354\203\201\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..e2ecf0ca
--- /dev/null
+++ "b/public/images/keywords/\354\235\264\354\203\201\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\264\354\234\240 \354\227\206\353\212\224 \354\235\274\354\227\220 \354\225\275\355\225\234.svg" "b/public/images/keywords/\354\235\264\354\234\240 \354\227\206\353\212\224 \354\235\274\354\227\220 \354\225\275\355\225\234.svg"
new file mode 100644
index 00000000..9c307813
--- /dev/null
+++ "b/public/images/keywords/\354\235\264\354\234\240 \354\227\206\353\212\224 \354\235\274\354\227\220 \354\225\275\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\270\354\202\254\354\235\264\355\212\270\352\260\200 \352\271\212\354\235\200.svg" "b/public/images/keywords/\354\235\270\354\202\254\354\235\264\355\212\270\352\260\200 \352\271\212\354\235\200.svg"
new file mode 100644
index 00000000..613b16a0
--- /dev/null
+++ "b/public/images/keywords/\354\235\270\354\202\254\354\235\264\355\212\270\352\260\200 \352\271\212\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\270\354\210\230\354\235\270\352\263\204\352\260\200 \353\252\205\355\231\225\355\225\234.svg" "b/public/images/keywords/\354\235\270\354\210\230\354\235\270\352\263\204\352\260\200 \353\252\205\355\231\225\355\225\234.svg"
new file mode 100644
index 00000000..b45812e6
--- /dev/null
+++ "b/public/images/keywords/\354\235\270\354\210\230\354\235\270\352\263\204\352\260\200 \353\252\205\355\231\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\352\264\200\354\204\261 \354\236\210\353\212\224.svg" "b/public/images/keywords/\354\235\274\352\264\200\354\204\261 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..6ba67cde
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\352\264\200\354\204\261 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\352\264\200\354\204\261\354\235\204 \352\263\240\353\257\274\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\235\274\352\264\200\354\204\261\354\235\204 \352\263\240\353\257\274\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..f292bf60
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\352\264\200\354\204\261\354\235\204 \352\263\240\353\257\274\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\352\264\200\354\204\261\354\235\264 \353\266\200\354\241\261\355\225\234.svg" "b/public/images/keywords/\354\235\274\352\264\200\354\204\261\354\235\264 \353\266\200\354\241\261\355\225\234.svg"
new file mode 100644
index 00000000..9bc761ce
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\352\264\200\354\204\261\354\235\264 \353\266\200\354\241\261\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\235\230 \353\263\270\354\247\210\354\235\204 \354\247\232\353\212\224.svg" "b/public/images/keywords/\354\235\274\354\235\230 \353\263\270\354\247\210\354\235\204 \354\247\232\353\212\224.svg"
new file mode 100644
index 00000000..564dc446
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\235\230 \353\263\270\354\247\210\354\235\204 \354\247\232\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\235\230 \354\210\234\354\204\234\353\245\274 \354\236\241\354\225\204\354\243\274\353\212\224.svg" "b/public/images/keywords/\354\235\274\354\235\230 \354\210\234\354\204\234\353\245\274 \354\236\241\354\225\204\354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..10f5e4df
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\235\230 \354\210\234\354\204\234\353\245\274 \354\236\241\354\225\204\354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\240\225 \352\264\200\353\246\254\352\260\200 \354\225\204\354\211\254\354\232\264.svg" "b/public/images/keywords/\354\235\274\354\240\225 \352\264\200\353\246\254\352\260\200 \354\225\204\354\211\254\354\232\264.svg"
new file mode 100644
index 00000000..f6a96baa
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\240\225 \352\264\200\353\246\254\352\260\200 \354\225\204\354\211\254\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\240\225 \352\264\200\353\246\254\352\260\200 \354\262\240\354\240\200\355\225\234.svg" "b/public/images/keywords/\354\235\274\354\240\225 \352\264\200\353\246\254\352\260\200 \354\262\240\354\240\200\355\225\234.svg"
new file mode 100644
index 00000000..1cfc17bd
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\240\225 \352\264\200\353\246\254\352\260\200 \354\262\240\354\240\200\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\240\225 \354\244\200\354\210\230\352\260\200 \354\225\204\354\211\254\354\232\264.svg" "b/public/images/keywords/\354\235\274\354\240\225 \354\244\200\354\210\230\352\260\200 \354\225\204\354\211\254\354\232\264.svg"
new file mode 100644
index 00000000..ee8bc436
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\240\225 \354\244\200\354\210\230\352\260\200 \354\225\204\354\211\254\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\240\225\353\263\264\353\213\244 \354\263\220\354\247\200\353\212\224.svg" "b/public/images/keywords/\354\235\274\354\240\225\353\263\264\353\213\244 \354\263\220\354\247\200\353\212\224.svg"
new file mode 100644
index 00000000..971a0d34
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\240\225\353\263\264\353\213\244 \354\263\220\354\247\200\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\240\225\354\235\264 \353\247\216\354\235\200.svg" "b/public/images/keywords/\354\235\274\354\240\225\354\235\264 \353\247\216\354\235\200.svg"
new file mode 100644
index 00000000..b4f324f4
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\240\225\354\235\264 \353\247\216\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\235\274\354\262\230\353\246\254\352\260\200 \353\271\240\353\245\270.svg" "b/public/images/keywords/\354\235\274\354\262\230\353\246\254\352\260\200 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..f2c8d6d3
--- /dev/null
+++ "b/public/images/keywords/\354\235\274\354\262\230\353\246\254\352\260\200 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260 \352\260\235\352\264\200\355\231\224\352\260\200 \353\220\230\353\212\224.svg" "b/public/images/keywords/\354\236\220\352\270\260 \352\260\235\352\264\200\355\231\224\352\260\200 \353\220\230\353\212\224.svg"
new file mode 100644
index 00000000..28db34e7
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260 \352\260\235\352\264\200\355\231\224\352\260\200 \353\220\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260 \352\264\200\353\246\254\352\260\200 \353\220\230\353\212\224.svg" "b/public/images/keywords/\354\236\220\352\270\260 \352\264\200\353\246\254\352\260\200 \353\220\230\353\212\224.svg"
new file mode 100644
index 00000000..fe846b76
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260 \352\264\200\353\246\254\352\260\200 \353\220\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260 \352\270\260\354\244\200\354\235\264 \352\260\225\355\225\234.svg" "b/public/images/keywords/\354\236\220\352\270\260 \352\270\260\354\244\200\354\235\264 \352\260\225\355\225\234.svg"
new file mode 100644
index 00000000..57a3a4cb
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260 \352\270\260\354\244\200\354\235\264 \352\260\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260 \354\204\261\355\226\245\354\227\220 \353\250\270\353\254\264\353\245\264\353\212\224.svg" "b/public/images/keywords/\354\236\220\352\270\260 \354\204\261\355\226\245\354\227\220 \353\250\270\353\254\264\353\245\264\353\212\224.svg"
new file mode 100644
index 00000000..bc7ffdaf
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260 \354\204\261\355\226\245\354\227\220 \353\250\270\353\254\264\353\245\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260 \355\216\230\354\235\264\354\212\244\354\227\220 \352\260\207\355\236\214.svg" "b/public/images/keywords/\354\236\220\352\270\260 \355\216\230\354\235\264\354\212\244\354\227\220 \352\260\207\355\236\214.svg"
new file mode 100644
index 00000000..ee45c5f2
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260 \355\216\230\354\235\264\354\212\244\354\227\220 \352\260\207\355\236\214.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260 \355\216\230\354\235\264\354\212\244\355\230\225\354\235\270.svg" "b/public/images/keywords/\354\236\220\352\270\260 \355\216\230\354\235\264\354\212\244\355\230\225\354\235\270.svg"
new file mode 100644
index 00000000..c82d5b94
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260 \355\216\230\354\235\264\354\212\244\355\230\225\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260\353\271\204\355\214\220\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\236\220\352\270\260\353\271\204\355\214\220\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..bb2b556e
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260\353\271\204\355\214\220\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\352\270\260\354\243\274\353\217\204\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\236\220\352\270\260\354\243\274\353\217\204\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..babf4dc2
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\352\270\260\354\243\274\353\217\204\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\354\213\240\352\260\220 \354\236\210\353\212\224.svg" "b/public/images/keywords/\354\236\220\354\213\240\352\260\220 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..bf20c5d3
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\354\213\240\352\260\220 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\220\354\213\240\352\260\220\354\235\264 \355\235\224\353\223\244\353\246\254\353\212\224.svg" "b/public/images/keywords/\354\236\220\354\213\240\352\260\220\354\235\264 \355\235\224\353\223\244\353\246\254\353\212\224.svg"
new file mode 100644
index 00000000..34930967
--- /dev/null
+++ "b/public/images/keywords/\354\236\220\354\213\240\352\260\220\354\235\264 \355\235\224\353\223\244\353\246\254\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\221\354\227\205 \354\206\215\353\217\204\352\260\200 \354\235\274\354\240\225\355\225\230\354\247\200 \354\225\212\354\235\200.svg" "b/public/images/keywords/\354\236\221\354\227\205 \354\206\215\353\217\204\352\260\200 \354\235\274\354\240\225\355\225\230\354\247\200 \354\225\212\354\235\200.svg"
new file mode 100644
index 00000000..51cd316a
--- /dev/null
+++ "b/public/images/keywords/\354\236\221\354\227\205 \354\206\215\353\217\204\352\260\200 \354\235\274\354\240\225\355\225\230\354\247\200 \354\225\212\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\236\221\354\227\205 \355\232\250\354\234\250\354\235\204 \353\206\222\354\235\264\353\212\224.svg" "b/public/images/keywords/\354\236\221\354\227\205 \355\232\250\354\234\250\354\235\204 \353\206\222\354\235\264\353\212\224.svg"
new file mode 100644
index 00000000..0cd9fea2
--- /dev/null
+++ "b/public/images/keywords/\354\236\221\354\227\205 \355\232\250\354\234\250\354\235\204 \353\206\222\354\235\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\240\201\352\267\271\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\240\201\352\267\271\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..afbb14a7
--- /dev/null
+++ "b/public/images/keywords/\354\240\201\352\267\271\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\240\204\353\213\254 \354\236\220\353\243\214\352\260\200 \352\271\224\353\201\224\355\225\234.svg" "b/public/images/keywords/\354\240\204\353\213\254 \354\236\220\353\243\214\352\260\200 \352\271\224\353\201\224\355\225\234.svg"
new file mode 100644
index 00000000..97288151
--- /dev/null
+++ "b/public/images/keywords/\354\240\204\353\213\254 \354\236\220\353\243\214\352\260\200 \352\271\224\353\201\224\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\240\225\353\246\254 \353\254\270\354\204\234\353\245\274 \354\236\230 \353\247\214\353\223\234\353\212\224.svg" "b/public/images/keywords/\354\240\225\353\246\254 \353\254\270\354\204\234\353\245\274 \354\236\230 \353\247\214\353\223\234\353\212\224.svg"
new file mode 100644
index 00000000..d15bbc50
--- /dev/null
+++ "b/public/images/keywords/\354\240\225\353\246\254 \353\254\270\354\204\234\353\245\274 \354\236\230 \353\247\214\353\223\234\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\240\225\353\246\254 \355\233\204 \353\247\220\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\240\225\353\246\254 \355\233\204 \353\247\220\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..c0d8a793
--- /dev/null
+++ "b/public/images/keywords/\354\240\225\353\246\254 \355\233\204 \353\247\220\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\240\225\353\246\254\355\225\264\354\204\234 \353\247\220\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\240\225\353\246\254\355\225\264\354\204\234 \353\247\220\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..8d65a418
--- /dev/null
+++ "b/public/images/keywords/\354\240\225\353\246\254\355\225\264\354\204\234 \353\247\220\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\240\225\353\263\264 \354\210\230\354\247\221\354\235\264 \353\271\240\353\245\270.svg" "b/public/images/keywords/\354\240\225\353\263\264 \354\210\230\354\247\221\354\235\264 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..b27563a5
--- /dev/null
+++ "b/public/images/keywords/\354\240\225\353\263\264 \354\210\230\354\247\221\354\235\264 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\241\260\354\232\251\355\225\234 \355\216\270\354\235\270.svg" "b/public/images/keywords/\354\241\260\354\232\251\355\225\234 \355\216\270\354\235\270.svg"
new file mode 100644
index 00000000..daff6166
--- /dev/null
+++ "b/public/images/keywords/\354\241\260\354\232\251\355\225\234 \355\216\270\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\241\264\354\244\221\354\235\264 \354\227\206\353\212\224.svg" "b/public/images/keywords/\354\241\264\354\244\221\354\235\264 \354\227\206\353\212\224.svg"
new file mode 100644
index 00000000..8de0426f
--- /dev/null
+++ "b/public/images/keywords/\354\241\264\354\244\221\354\235\264 \354\227\206\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\244\200\353\271\204\352\260\200 \352\263\274\353\217\204\355\225\234.svg" "b/public/images/keywords/\354\244\200\353\271\204\352\260\200 \352\263\274\353\217\204\355\225\234.svg"
new file mode 100644
index 00000000..fff40719
--- /dev/null
+++ "b/public/images/keywords/\354\244\200\353\271\204\352\260\200 \352\263\274\353\217\204\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\244\200\353\271\204\352\260\200 \352\270\264.svg" "b/public/images/keywords/\354\244\200\353\271\204\352\260\200 \352\270\264.svg"
new file mode 100644
index 00000000..b438cef9
--- /dev/null
+++ "b/public/images/keywords/\354\244\200\353\271\204\352\260\200 \352\270\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\244\200\353\271\204\354\204\261\354\235\264 \354\262\240\354\240\200\355\225\234.svg" "b/public/images/keywords/\354\244\200\353\271\204\354\204\261\354\235\264 \354\262\240\354\240\200\355\225\234.svg"
new file mode 100644
index 00000000..4a80a8d8
--- /dev/null
+++ "b/public/images/keywords/\354\244\200\353\271\204\354\204\261\354\235\264 \354\262\240\354\240\200\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\244\221\353\246\275\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\244\221\353\246\275\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..9b306c98
--- /dev/null
+++ "b/public/images/keywords/\354\244\221\353\246\275\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\244\221\354\213\254\354\235\204 \354\236\230 \354\236\241\354\235\200.svg" "b/public/images/keywords/\354\244\221\354\213\254\354\235\204 \354\236\230 \354\236\241\354\235\200.svg"
new file mode 100644
index 00000000..3d56eca6
--- /dev/null
+++ "b/public/images/keywords/\354\244\221\354\213\254\354\235\204 \354\236\230 \354\236\241\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\244\221\354\213\254\354\235\204 \354\236\241\354\225\204\354\243\274\353\212\224.svg" "b/public/images/keywords/\354\244\221\354\213\254\354\235\204 \354\236\241\354\225\204\354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..d2cb5cc4
--- /dev/null
+++ "b/public/images/keywords/\354\244\221\354\213\254\354\235\204 \354\236\241\354\225\204\354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\244\221\354\236\254\353\245\274 \354\236\230\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\244\221\354\236\254\353\245\274 \354\236\230\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..0a79ecc3
--- /dev/null
+++ "b/public/images/keywords/\354\244\221\354\236\254\353\245\274 \354\236\230\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\246\211\352\260\201 \353\214\200\354\235\221\354\235\264 \353\212\220\353\246\260.svg" "b/public/images/keywords/\354\246\211\352\260\201 \353\214\200\354\235\221\354\235\264 \353\212\220\353\246\260.svg"
new file mode 100644
index 00000000..dc38374c
--- /dev/null
+++ "b/public/images/keywords/\354\246\211\352\260\201 \353\214\200\354\235\221\354\235\264 \353\212\220\353\246\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\246\211\355\235\245\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\246\211\355\235\245\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..ad369e0f
--- /dev/null
+++ "b/public/images/keywords/\354\246\211\355\235\245\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\247\201\352\264\200\354\227\220 \354\235\230\354\241\264\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\247\201\352\264\200\354\227\220 \354\235\230\354\241\264\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..4ab8ab56
--- /dev/null
+++ "b/public/images/keywords/\354\247\201\352\264\200\354\227\220 \354\235\230\354\241\264\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\247\201\352\264\200\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\247\201\352\264\200\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..283f4308
--- /dev/null
+++ "b/public/images/keywords/\354\247\201\352\264\200\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\247\204\355\226\211 \354\203\201\355\231\251\354\235\204 \352\263\265\354\234\240\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\247\204\355\226\211 \354\203\201\355\231\251\354\235\204 \352\263\265\354\234\240\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..cd967683
--- /dev/null
+++ "b/public/images/keywords/\354\247\204\355\226\211 \354\203\201\355\231\251\354\235\204 \352\263\265\354\234\240\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\247\221\354\244\221 \353\266\204\354\202\260\354\240\201\354\235\270.svg" "b/public/images/keywords/\354\247\221\354\244\221 \353\266\204\354\202\260\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..c45165b1
--- /dev/null
+++ "b/public/images/keywords/\354\247\221\354\244\221 \353\266\204\354\202\260\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\247\221\354\244\221\353\240\245\354\235\264 \354\242\213\354\235\200.svg" "b/public/images/keywords/\354\247\221\354\244\221\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
new file mode 100644
index 00000000..369fdebe
--- /dev/null
+++ "b/public/images/keywords/\354\247\221\354\244\221\353\240\245\354\235\264 \354\242\213\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\247\221\354\244\221\354\235\264 \353\271\240\353\245\270.svg" "b/public/images/keywords/\354\247\221\354\244\221\354\235\264 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..a12bef70
--- /dev/null
+++ "b/public/images/keywords/\354\247\221\354\244\221\354\235\264 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\260\250\353\266\204\355\225\230\352\262\214 \354\210\230\354\232\251\355\225\234.svg" "b/public/images/keywords/\354\260\250\353\266\204\355\225\230\352\262\214 \354\210\230\354\232\251\355\225\234.svg"
new file mode 100644
index 00000000..ab4791ee
--- /dev/null
+++ "b/public/images/keywords/\354\260\250\353\266\204\355\225\230\352\262\214 \354\210\230\354\232\251\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\260\251\354\213\244\355\225\234.svg" "b/public/images/keywords/\354\260\251\354\213\244\355\225\234.svg"
new file mode 100644
index 00000000..5f131500
--- /dev/null
+++ "b/public/images/keywords/\354\260\251\354\213\244\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\260\275\354\235\230\354\240\201 \352\264\200\354\240\220\354\235\204 \354\240\234\354\213\234\355\225\230\353\212\224.svg" "b/public/images/keywords/\354\260\275\354\235\230\354\240\201 \352\264\200\354\240\220\354\235\204 \354\240\234\354\213\234\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..5043ba3a
--- /dev/null
+++ "b/public/images/keywords/\354\260\275\354\235\230\354\240\201 \352\264\200\354\240\220\354\235\204 \354\240\234\354\213\234\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\261\205\354\236\204\352\260\220 \354\236\210\353\212\224.svg" "b/public/images/keywords/\354\261\205\354\236\204\352\260\220 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..e87b6614
--- /dev/null
+++ "b/public/images/keywords/\354\261\205\354\236\204\352\260\220 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\261\205\354\236\204\352\260\220\354\235\264 \354\225\275\355\225\234.svg" "b/public/images/keywords/\354\261\205\354\236\204\352\260\220\354\235\264 \354\225\275\355\225\234.svg"
new file mode 100644
index 00000000..079f4266
--- /dev/null
+++ "b/public/images/keywords/\354\261\205\354\236\204\352\260\220\354\235\264 \354\225\275\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\266\224\354\247\204\353\240\245 \354\236\210\353\212\224.svg" "b/public/images/keywords/\354\266\224\354\247\204\353\240\245 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..2e73652e
--- /dev/null
+++ "b/public/images/keywords/\354\266\224\354\247\204\353\240\245 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\271\250\354\260\251\355\225\234.svg" "b/public/images/keywords/\354\271\250\354\260\251\355\225\234.svg"
new file mode 100644
index 00000000..45db842c
--- /dev/null
+++ "b/public/images/keywords/\354\271\250\354\260\251\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\273\244\353\256\244\353\213\210\354\274\200\354\235\264\354\205\230\354\235\264 \353\233\260\354\226\264\353\202\234.svg" "b/public/images/keywords/\354\273\244\353\256\244\353\213\210\354\274\200\354\235\264\354\205\230\354\235\264 \353\233\260\354\226\264\353\202\234.svg"
new file mode 100644
index 00000000..9cd477a4
--- /dev/null
+++ "b/public/images/keywords/\354\273\244\353\256\244\353\213\210\354\274\200\354\235\264\354\205\230\354\235\264 \353\233\260\354\226\264\353\202\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\354\273\250\353\224\224\354\205\230 \354\230\201\355\226\245\354\235\264 \355\201\260.svg" "b/public/images/keywords/\354\273\250\353\224\224\354\205\230 \354\230\201\355\226\245\354\235\264 \355\201\260.svg"
new file mode 100644
index 00000000..867841a1
--- /dev/null
+++ "b/public/images/keywords/\354\273\250\353\224\224\354\205\230 \354\230\201\355\226\245\354\235\264 \355\201\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\201\260 \352\267\270\353\246\274\354\235\204 \353\263\264\353\212\224.svg" "b/public/images/keywords/\355\201\260 \352\267\270\353\246\274\354\235\204 \353\263\264\353\212\224.svg"
new file mode 100644
index 00000000..0bc65eed
--- /dev/null
+++ "b/public/images/keywords/\355\201\260 \352\267\270\353\246\274\354\235\204 \353\263\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\203\200\354\235\270\354\235\204 \354\241\264\354\244\221\355\225\230\353\212\224.svg" "b/public/images/keywords/\355\203\200\354\235\270\354\235\204 \354\241\264\354\244\221\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..864d6b30
--- /dev/null
+++ "b/public/images/keywords/\355\203\200\354\235\270\354\235\204 \354\241\264\354\244\221\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\203\200\355\230\221\354\227\220 \354\225\204\354\211\254\354\232\264.svg" "b/public/images/keywords/\355\203\200\355\230\221\354\227\220 \354\225\204\354\211\254\354\232\264.svg"
new file mode 100644
index 00000000..1ee8001d
--- /dev/null
+++ "b/public/images/keywords/\355\203\200\355\230\221\354\227\220 \354\225\204\354\211\254\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\203\200\355\230\221\354\235\264 \354\226\264\353\240\244\354\232\264.svg" "b/public/images/keywords/\355\203\200\355\230\221\354\235\264 \354\226\264\353\240\244\354\232\264.svg"
new file mode 100644
index 00000000..e940ff5d
--- /dev/null
+++ "b/public/images/keywords/\355\203\200\355\230\221\354\235\264 \354\226\264\353\240\244\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\203\234\353\217\204\352\260\200 \354\204\261\354\210\231\355\225\234.svg" "b/public/images/keywords/\355\203\234\353\217\204\352\260\200 \354\204\261\354\210\231\355\225\234.svg"
new file mode 100644
index 00000000..31eecdc1
--- /dev/null
+++ "b/public/images/keywords/\355\203\234\353\217\204\352\260\200 \354\204\261\354\210\231\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\203\234\353\217\204\352\260\200 \354\235\274\352\264\200\353\220\234.svg" "b/public/images/keywords/\355\203\234\353\217\204\352\260\200 \354\235\274\352\264\200\353\220\234.svg"
new file mode 100644
index 00000000..8dc51959
--- /dev/null
+++ "b/public/images/keywords/\355\203\234\353\217\204\352\260\200 \354\235\274\352\264\200\353\220\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\203\234\353\217\204\353\245\274 \354\240\220\352\262\200\355\225\230\353\212\224.svg" "b/public/images/keywords/\355\203\234\353\217\204\353\245\274 \354\240\220\352\262\200\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..7258a081
--- /dev/null
+++ "b/public/images/keywords/\355\203\234\353\217\204\353\245\274 \354\240\220\352\262\200\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\206\265\354\260\260\353\240\245 \354\236\210\353\212\224.svg" "b/public/images/keywords/\355\206\265\354\260\260\353\240\245 \354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..51944160
--- /dev/null
+++ "b/public/images/keywords/\355\206\265\354\260\260\353\240\245 \354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\210\264 \355\231\234\354\232\251\354\235\264 \353\212\245\354\210\231\355\225\234.svg" "b/public/images/keywords/\355\210\264 \355\231\234\354\232\251\354\235\264 \353\212\245\354\210\231\355\225\234.svg"
new file mode 100644
index 00000000..c1d27fd2
--- /dev/null
+++ "b/public/images/keywords/\355\210\264 \355\231\234\354\232\251\354\235\264 \353\212\245\354\210\231\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\212\270\353\240\214\353\223\234\354\227\220 \353\257\274\352\260\220\355\225\234.svg" "b/public/images/keywords/\355\212\270\353\240\214\353\223\234\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
new file mode 100644
index 00000000..e13e3cbf
--- /dev/null
+++ "b/public/images/keywords/\355\212\270\353\240\214\353\223\234\354\227\220 \353\257\274\352\260\220\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200 \353\252\260\354\236\205\353\217\204\353\245\274 \353\206\222\354\235\264\353\212\224.svg" "b/public/images/keywords/\355\214\200 \353\252\260\354\236\205\353\217\204\353\245\274 \353\206\222\354\235\264\353\212\224.svg"
new file mode 100644
index 00000000..a81ffe8a
--- /dev/null
+++ "b/public/images/keywords/\355\214\200 \353\252\260\354\236\205\353\217\204\353\245\274 \353\206\222\354\235\264\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200 \353\266\204\354\234\204\352\270\260\353\245\274 \354\240\225\353\217\210\355\225\230\353\212\224.svg" "b/public/images/keywords/\355\214\200 \353\266\204\354\234\204\352\270\260\353\245\274 \354\240\225\353\217\210\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..67362bfb
--- /dev/null
+++ "b/public/images/keywords/\355\214\200 \353\266\204\354\234\204\352\270\260\353\245\274 \354\240\225\353\217\210\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\227\220 \354\225\210\354\240\225\352\260\220\354\235\204 \354\243\274\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\227\220 \354\225\210\354\240\225\352\260\220\354\235\204 \354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..30b35cfe
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\227\220 \354\225\210\354\240\225\352\260\220\354\235\204 \354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\227\220 \354\227\254\354\234\240\353\245\274 \353\247\214\353\223\234\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\227\220 \354\227\254\354\234\240\353\245\274 \353\247\214\353\223\234\353\212\224.svg"
new file mode 100644
index 00000000..41055eef
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\227\220 \354\227\254\354\234\240\353\245\274 \353\247\214\353\223\234\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\227\220 \354\230\250\352\270\260\353\245\274 \353\215\224\355\225\230\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\227\220 \354\230\250\352\270\260\353\245\274 \353\215\224\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..f503ea27
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\227\220 \354\230\250\352\270\260\353\245\274 \353\215\224\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\227\220 \355\231\234\353\240\245\354\235\204 \354\243\274\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\227\220 \355\231\234\353\240\245\354\235\204 \354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..4ee85f1b
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\227\220 \355\231\234\353\240\245\354\235\204 \354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\233\220 \354\204\261\354\236\245\354\235\204 \353\217\225\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\233\220 \354\204\261\354\236\245\354\235\204 \353\217\225\353\212\224.svg"
new file mode 100644
index 00000000..2b0e284f
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\233\220 \354\204\261\354\236\245\354\235\204 \353\217\225\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\235\204 \354\236\230 \354\261\231\352\270\260\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\235\204 \354\236\230 \354\261\231\352\270\260\353\212\224.svg"
new file mode 100644
index 00000000..339bb0b8
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\235\204 \354\236\230 \354\261\231\352\270\260\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\235\230 \352\267\240\355\230\225\354\235\204 \353\247\236\354\266\224\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\235\230 \352\267\240\355\230\225\354\235\204 \353\247\236\354\266\224\353\212\224.svg"
new file mode 100644
index 00000000..e544f0c6
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\235\230 \352\267\240\355\230\225\354\235\204 \353\247\236\354\266\224\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\235\230 \353\246\254\353\223\254\354\235\204 \353\247\236\354\266\230.svg" "b/public/images/keywords/\355\214\200\354\235\230 \353\246\254\353\223\254\354\235\204 \353\247\236\354\266\230.svg"
new file mode 100644
index 00000000..159a3339
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\235\230 \353\246\254\353\223\254\354\235\204 \353\247\236\354\266\230.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\235\230 \353\266\200\353\213\264\354\235\204 \353\202\230\353\210\204\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\235\230 \353\266\200\353\213\264\354\235\204 \353\202\230\353\210\204\353\212\224.svg"
new file mode 100644
index 00000000..9003ac31
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\235\230 \353\266\200\353\213\264\354\235\204 \353\202\230\353\210\204\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\235\230 \354\206\215\353\217\204\353\245\274 \353\247\236\354\266\224\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\235\230 \354\206\215\353\217\204\353\245\274 \353\247\236\354\266\224\353\212\224.svg"
new file mode 100644
index 00000000..d3518a2d
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\235\230 \354\206\215\353\217\204\353\245\274 \353\247\236\354\266\224\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\200\354\235\230 \354\213\240\353\242\260 \354\236\220\354\202\260\354\235\264 \353\220\230\353\212\224.svg" "b/public/images/keywords/\355\214\200\354\235\230 \354\213\240\353\242\260 \354\236\220\354\202\260\354\235\264 \353\220\230\353\212\224.svg"
new file mode 100644
index 00000000..73e4e656
--- /dev/null
+++ "b/public/images/keywords/\355\214\200\354\235\230 \354\213\240\353\242\260 \354\236\220\354\202\260\354\235\264 \353\220\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\220\353\213\250 \352\267\274\352\261\260\352\260\200 \353\266\204\353\252\205\355\225\234.svg" "b/public/images/keywords/\355\214\220\353\213\250 \352\267\274\352\261\260\352\260\200 \353\266\204\353\252\205\355\225\234.svg"
new file mode 100644
index 00000000..5c9654fd
--- /dev/null
+++ "b/public/images/keywords/\355\214\220\353\213\250 \352\267\274\352\261\260\352\260\200 \353\266\204\353\252\205\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\220\353\213\250\354\227\220 \354\261\205\354\236\204\354\247\200\353\212\224.svg" "b/public/images/keywords/\355\214\220\353\213\250\354\227\220 \354\261\205\354\236\204\354\247\200\353\212\224.svg"
new file mode 100644
index 00000000..48623761
--- /dev/null
+++ "b/public/images/keywords/\355\214\220\353\213\250\354\227\220 \354\261\205\354\236\204\354\247\200\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\220\353\213\250\354\235\204 \353\257\270\353\243\250\353\212\224.svg" "b/public/images/keywords/\355\214\220\353\213\250\354\235\204 \353\257\270\353\243\250\353\212\224.svg"
new file mode 100644
index 00000000..f124de3a
--- /dev/null
+++ "b/public/images/keywords/\355\214\220\353\213\250\354\235\204 \353\257\270\353\243\250\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\214\220\353\213\250\354\235\264 \353\252\205\355\231\225\355\225\234.svg" "b/public/images/keywords/\355\214\220\353\213\250\354\235\264 \353\252\205\355\231\225\355\225\234.svg"
new file mode 100644
index 00000000..094a78a9
--- /dev/null
+++ "b/public/images/keywords/\355\214\220\353\213\250\354\235\264 \353\252\205\355\231\225\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\216\270\352\262\254\354\236\210\353\212\224.svg" "b/public/images/keywords/\355\216\270\352\262\254\354\236\210\353\212\224.svg"
new file mode 100644
index 00000000..c35a4e67
--- /dev/null
+++ "b/public/images/keywords/\355\216\270\352\262\254\354\236\210\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\216\270\355\225\230\352\262\214 \353\247\214\353\223\234\353\212\224.svg" "b/public/images/keywords/\355\216\270\355\225\230\352\262\214 \353\247\214\353\223\234\353\212\224.svg"
new file mode 100644
index 00000000..6c78feba
--- /dev/null
+++ "b/public/images/keywords/\355\216\270\355\225\230\352\262\214 \353\247\214\353\223\234\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\217\254\354\232\251\354\240\201\354\235\270.svg" "b/public/images/keywords/\355\217\254\354\232\251\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..7bb013ea
--- /dev/null
+++ "b/public/images/keywords/\355\217\254\354\232\251\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\221\234\355\230\204\354\235\264 \353\213\250\354\241\260\353\241\234\354\232\264.svg" "b/public/images/keywords/\355\221\234\355\230\204\354\235\264 \353\213\250\354\241\260\353\241\234\354\232\264.svg"
new file mode 100644
index 00000000..612832e8
--- /dev/null
+++ "b/public/images/keywords/\355\221\234\355\230\204\354\235\264 \353\213\250\354\241\260\353\241\234\354\232\264.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\224\274\353\223\234\353\260\261 \353\260\230\354\230\201\354\235\264 \353\212\220\353\246\260.svg" "b/public/images/keywords/\355\224\274\353\223\234\353\260\261 \353\260\230\354\230\201\354\235\264 \353\212\220\353\246\260.svg"
new file mode 100644
index 00000000..b8afe2c9
--- /dev/null
+++ "b/public/images/keywords/\355\224\274\353\223\234\353\260\261 \353\260\230\354\230\201\354\235\264 \353\212\220\353\246\260.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \352\261\264\354\204\244\354\240\201\354\234\274\353\241\234 \354\243\274\353\212\224.svg" "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \352\261\264\354\204\244\354\240\201\354\234\274\353\241\234 \354\243\274\353\212\224.svg"
new file mode 100644
index 00000000..43d8cd62
--- /dev/null
+++ "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \352\261\264\354\204\244\354\240\201\354\234\274\353\241\234 \354\243\274\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \352\263\261\354\224\271\353\212\224.svg" "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \352\263\261\354\224\271\353\212\224.svg"
new file mode 100644
index 00000000..22cdd05c
--- /dev/null
+++ "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \352\263\261\354\224\271\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \354\227\264\353\246\260 \355\203\234\353\217\204\353\241\234 \353\260\233\353\212\224.svg" "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \354\227\264\353\246\260 \355\203\234\353\217\204\353\241\234 \353\260\233\353\212\224.svg"
new file mode 100644
index 00000000..23477d02
--- /dev/null
+++ "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\204 \354\227\264\353\246\260 \355\203\234\353\217\204\353\241\234 \353\260\233\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\264 \353\271\240\353\245\270.svg" "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\264 \353\271\240\353\245\270.svg"
new file mode 100644
index 00000000..d2b06a5f
--- /dev/null
+++ "b/public/images/keywords/\355\224\274\353\223\234\353\260\261\354\235\264 \353\271\240\353\245\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\225\231\354\212\265 \354\235\230\354\247\200\352\260\200 \353\206\222\354\235\200.svg" "b/public/images/keywords/\355\225\231\354\212\265 \354\235\230\354\247\200\352\260\200 \353\206\222\354\235\200.svg"
new file mode 100644
index 00000000..8897d6d5
--- /dev/null
+++ "b/public/images/keywords/\355\225\231\354\212\265 \354\235\230\354\247\200\352\260\200 \353\206\222\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\225\250\352\273\230 \354\204\261\354\236\245\355\225\230\352\262\214 \355\225\230\353\212\224.svg" "b/public/images/keywords/\355\225\250\352\273\230 \354\204\261\354\236\245\355\225\230\352\262\214 \355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..06706212
--- /dev/null
+++ "b/public/images/keywords/\355\225\250\352\273\230 \354\204\261\354\236\245\355\225\230\352\262\214 \355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\225\250\352\273\230 \354\235\274\355\225\230\352\270\260 \355\216\270\355\225\234.svg" "b/public/images/keywords/\355\225\250\352\273\230 \354\235\274\355\225\230\352\270\260 \355\216\270\355\225\234.svg"
new file mode 100644
index 00000000..fbd30846
--- /dev/null
+++ "b/public/images/keywords/\355\225\250\352\273\230 \354\235\274\355\225\230\352\270\260 \355\216\270\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\225\251\354\235\230\353\245\274 \354\236\230 \354\235\264\353\201\210.svg" "b/public/images/keywords/\355\225\251\354\235\230\353\245\274 \354\236\230 \354\235\264\353\201\210.svg"
new file mode 100644
index 00000000..8f966723
--- /dev/null
+++ "b/public/images/keywords/\355\225\251\354\235\230\353\245\274 \354\236\230 \354\235\264\353\201\210.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\226\211\353\217\231 \354\244\221\354\213\254\354\240\201\354\235\270.svg" "b/public/images/keywords/\355\226\211\353\217\231 \354\244\221\354\213\254\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..8c14c21a
--- /dev/null
+++ "b/public/images/keywords/\355\226\211\353\217\231 \354\244\221\354\213\254\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\204\354\213\244 \352\260\220\352\260\201\354\235\264 \353\233\260\354\226\264\353\202\234.svg" "b/public/images/keywords/\355\230\204\354\213\244 \352\260\220\352\260\201\354\235\264 \353\233\260\354\226\264\353\202\234.svg"
new file mode 100644
index 00000000..024061b3
--- /dev/null
+++ "b/public/images/keywords/\355\230\204\354\213\244 \352\260\220\352\260\201\354\235\264 \353\233\260\354\226\264\353\202\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\204\354\213\244\354\227\220 \354\225\210\354\243\274\355\225\230\353\212\224.svg" "b/public/images/keywords/\355\230\204\354\213\244\354\227\220 \354\225\210\354\243\274\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..beff700f
--- /dev/null
+++ "b/public/images/keywords/\355\230\204\354\213\244\354\227\220 \354\225\210\354\243\274\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\204\354\213\244\354\240\201\354\235\270.svg" "b/public/images/keywords/\355\230\204\354\213\244\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..556d194b
--- /dev/null
+++ "b/public/images/keywords/\355\230\204\354\213\244\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\221\354\203\201\354\227\220 \353\212\245\355\225\234.svg" "b/public/images/keywords/\355\230\221\354\203\201\354\227\220 \353\212\245\355\225\234.svg"
new file mode 100644
index 00000000..ccfe6205
--- /dev/null
+++ "b/public/images/keywords/\355\230\221\354\203\201\354\227\220 \353\212\245\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\221\354\227\205 \354\234\240\354\227\260\354\204\261\354\235\264 \353\202\256\354\235\200.svg" "b/public/images/keywords/\355\230\221\354\227\205 \354\234\240\354\227\260\354\204\261\354\235\264 \353\202\256\354\235\200.svg"
new file mode 100644
index 00000000..616ab9b3
--- /dev/null
+++ "b/public/images/keywords/\355\230\221\354\227\205 \354\234\240\354\227\260\354\204\261\354\235\264 \353\202\256\354\235\200.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\221\354\227\205\352\262\275\352\263\204\352\260\200 \353\266\210\353\266\204\353\252\205\355\225\234.svg" "b/public/images/keywords/\355\230\221\354\227\205\352\262\275\352\263\204\352\260\200 \353\266\210\353\266\204\353\252\205\355\225\234.svg"
new file mode 100644
index 00000000..a79349db
--- /dev/null
+++ "b/public/images/keywords/\355\230\221\354\227\205\352\262\275\352\263\204\352\260\200 \353\266\210\353\266\204\353\252\205\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\274\354\236\220 \355\225\264\352\262\260\355\225\230\353\240\244\353\212\224.svg" "b/public/images/keywords/\355\230\274\354\236\220 \355\225\264\352\262\260\355\225\230\353\240\244\353\212\224.svg"
new file mode 100644
index 00000000..ef6cd8ee
--- /dev/null
+++ "b/public/images/keywords/\355\230\274\354\236\220 \355\225\264\352\262\260\355\225\230\353\240\244\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\230\274\354\236\220 \355\232\214\353\263\265\355\225\230\353\212\224.svg" "b/public/images/keywords/\355\230\274\354\236\220 \355\232\214\353\263\265\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..6ffc9220
--- /dev/null
+++ "b/public/images/keywords/\355\230\274\354\236\220 \355\232\214\353\263\265\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\232\214\353\263\265\354\227\220 \354\213\234\352\260\204\354\235\264 \355\225\204\354\232\224\355\225\234.svg" "b/public/images/keywords/\355\232\214\353\263\265\354\227\220 \354\213\234\352\260\204\354\235\264 \355\225\204\354\232\224\355\225\234.svg"
new file mode 100644
index 00000000..af20a389
--- /dev/null
+++ "b/public/images/keywords/\355\232\214\353\263\265\354\227\220 \354\213\234\352\260\204\354\235\264 \355\225\204\354\232\224\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\232\214\355\224\274\355\225\230\353\212\224.svg" "b/public/images/keywords/\355\232\214\355\224\274\355\225\230\353\212\224.svg"
new file mode 100644
index 00000000..2da9b1ce
--- /dev/null
+++ "b/public/images/keywords/\355\232\214\355\224\274\355\225\230\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\232\250\354\234\250\354\240\201\354\235\270.svg" "b/public/images/keywords/\355\232\250\354\234\250\354\240\201\354\235\270.svg"
new file mode 100644
index 00000000..b5c47fdd
--- /dev/null
+++ "b/public/images/keywords/\355\232\250\354\234\250\354\240\201\354\235\270.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\234\264\354\213\235 \352\264\200\353\246\254\352\260\200 \354\225\275\355\225\234.svg" "b/public/images/keywords/\355\234\264\354\213\235 \352\264\200\353\246\254\352\260\200 \354\225\275\355\225\234.svg"
new file mode 100644
index 00000000..6b6a70d6
--- /dev/null
+++ "b/public/images/keywords/\355\234\264\354\213\235 \352\264\200\353\246\254\352\260\200 \354\225\275\355\225\234.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\235\220\353\246\204\354\235\204 \354\236\230 \354\235\275\353\212\224.svg" "b/public/images/keywords/\355\235\220\353\246\204\354\235\204 \354\236\230 \354\235\275\353\212\224.svg"
new file mode 100644
index 00000000..88e97354
--- /dev/null
+++ "b/public/images/keywords/\355\235\220\353\246\204\354\235\204 \354\236\230 \354\235\275\353\212\224.svg"
@@ -0,0 +1,22 @@
+
diff --git "a/public/images/keywords/\355\235\220\355\212\270\353\237\254\354\247\200\354\247\200 \354\225\212\353\212\224.svg" "b/public/images/keywords/\355\235\220\355\212\270\353\237\254\354\247\200\354\247\200 \354\225\212\353\212\224.svg"
new file mode 100644
index 00000000..8cc72ef8
--- /dev/null
+++ "b/public/images/keywords/\355\235\220\355\212\270\353\237\254\354\247\200\354\247\200 \354\225\212\353\212\224.svg"
@@ -0,0 +1,22 @@
+
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/Teamficial_metatag_Image.jpg b/public/og/Teamficial_metatag_Image.jpg
new file mode 100644
index 00000000..d7fde013
Binary files /dev/null and b/public/og/Teamficial_metatag_Image.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/src/app/(auth)/(callback)/google/oauth/GoogleCallback.tsx b/src/app/(auth)/(callback)/google/oauth/GoogleCallback.tsx
new file mode 100644
index 00000000..acd2d3a5
--- /dev/null
+++ b/src/app/(auth)/(callback)/google/oauth/GoogleCallback.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import { useEffect } from 'react';
+import { useRouter, useSearchParams } from 'next/navigation';
+import api from '@/libs/api/api';
+import { useUserStore } from '@/store/useUserStore';
+import { setCookie } from '@/utils/cookie';
+
+export default function GoogleCallbackClient() {
+ const router = useRouter();
+ const params = useSearchParams();
+ const { setUser } = useUserStore();
+
+ useEffect(() => {
+ const code = params.get('code');
+
+ if (code) {
+ api
+ .post('/auth/google', null, {
+ params: {
+ accessCode: code,
+ redirectUri: process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI,
+ },
+ })
+ .then((res) => {
+ const result = res.data?.result;
+ if (!result) {
+ router.replace('/login');
+ return;
+ }
+
+ const { userId, accessToken, refreshToken, uuid, userName } = result;
+
+ if (res.data.code === '200') {
+ setUser({ uuid, userId, userName });
+
+ localStorage.setItem('accessToken', accessToken);
+ localStorage.setItem('refreshToken', refreshToken);
+ setCookie('lastLoginProvider', 'google', { expires: 365 });
+ const redirectPath = localStorage.getItem('redirectAfterLogin');
+ localStorage.removeItem('redirectAfterLogin');
+
+ router.replace(redirectPath || '/');
+ } else {
+ router.replace('/login');
+ }
+ })
+ .catch((err) => {
+ console.log(err);
+ router.replace('/login');
+ });
+ }
+ }, [params, router, setUser]);
+
+ return null;
+}
diff --git a/src/app/(auth)/(callback)/google/oauth/page.tsx b/src/app/(auth)/(callback)/google/oauth/page.tsx
new file mode 100644
index 00000000..d6d37114
--- /dev/null
+++ b/src/app/(auth)/(callback)/google/oauth/page.tsx
@@ -0,0 +1,16 @@
+import { Suspense } from 'react';
+import GoogleCallbackClient from './GoogleCallback';
+
+export default function Page() {
+ return (
+
+
+
+ }
+ >
+
+
+ );
+}
diff --git a/src/app/(auth)/(callback)/kakao/oauth/KakaoCallback.tsx b/src/app/(auth)/(callback)/kakao/oauth/KakaoCallback.tsx
new file mode 100644
index 00000000..f511f6a1
--- /dev/null
+++ b/src/app/(auth)/(callback)/kakao/oauth/KakaoCallback.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import { useEffect } from 'react';
+import { useRouter, useSearchParams } from 'next/navigation';
+import api from '@/libs/api/api';
+import { useUserStore } from '@/store/useUserStore';
+import { setCookie } from '@/utils/cookie';
+
+export default function KakaoCallbackClient() {
+ const router = useRouter();
+ const params = useSearchParams();
+ const { setUser } = useUserStore();
+
+ useEffect(() => {
+ const code = params.get('code');
+
+ if (code) {
+ api
+ .post('/auth/kakao', null, {
+ params: {
+ accessCode: code,
+ redirectUri: process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI,
+ },
+ })
+ .then((res) => {
+ const result = res.data?.result;
+ if (!result) {
+ router.replace('/login');
+ return;
+ }
+
+ const { userId, accessToken, refreshToken, uuid, userName } = result;
+
+ if (res.data.code === '200') {
+ setUser({ uuid, userId, userName });
+
+ localStorage.setItem('accessToken', accessToken);
+ localStorage.setItem('refreshToken', refreshToken);
+ setCookie('lastLoginProvider', 'kakao', { expires: 365 });
+ const redirectPath = localStorage.getItem('redirectAfterLogin');
+ localStorage.removeItem('redirectAfterLogin');
+
+ router.replace(redirectPath || '/');
+ } else {
+ router.replace('/login');
+ }
+ })
+ .catch((err) => {
+ console.log(err);
+ router.replace('/login');
+ });
+ }
+ }, [params, router, setUser]);
+
+ return null;
+}
diff --git a/src/app/(auth)/(callback)/kakao/oauth/page.tsx b/src/app/(auth)/(callback)/kakao/oauth/page.tsx
new file mode 100644
index 00000000..63fedc9a
--- /dev/null
+++ b/src/app/(auth)/(callback)/kakao/oauth/page.tsx
@@ -0,0 +1,16 @@
+import { Suspense } from 'react';
+import KakaoCallbackClient from './KakaoCallback';
+
+export default function Page() {
+ return (
+
+
+
+ }
+ >
+
+
+ );
+}
diff --git a/src/app/(auth)/(callback)/naver/oauth/NaverCallback.tsx b/src/app/(auth)/(callback)/naver/oauth/NaverCallback.tsx
new file mode 100644
index 00000000..d56d8acf
--- /dev/null
+++ b/src/app/(auth)/(callback)/naver/oauth/NaverCallback.tsx
@@ -0,0 +1,58 @@
+'use client';
+
+import { useEffect } from 'react';
+import { useRouter, useSearchParams } from 'next/navigation';
+import api from '@/libs/api/api';
+import { useUserStore } from '@/store/useUserStore';
+import { setCookie } from '@/utils/cookie';
+
+export default function NaverCallbackClient() {
+ const router = useRouter();
+ const params = useSearchParams();
+ const { setUser } = useUserStore();
+
+ useEffect(() => {
+ const code = params.get('code');
+ const state = params.get('state');
+
+ if (code) {
+ api
+ .post('/auth/naver', null, {
+ params: {
+ code: code,
+ state: state,
+ redirectUri: process.env.NEXT_PUBLIC_NAVER_REDIRECT_URI,
+ },
+ })
+ .then((res) => {
+ const result = res.data?.result;
+ if (!result) {
+ router.replace('/login');
+ return;
+ }
+
+ const { userId, accessToken, refreshToken, uuid, userName } = result;
+
+ if (res.data.code === '200') {
+ setUser({ uuid, userId, userName });
+
+ localStorage.setItem('accessToken', accessToken);
+ localStorage.setItem('refreshToken', refreshToken);
+ setCookie('lastLoginProvider', 'naver', { expires: 365 });
+ const redirectPath = localStorage.getItem('redirectAfterLogin');
+ localStorage.removeItem('redirectAfterLogin');
+
+ router.replace(redirectPath || '/');
+ } else {
+ router.replace('/login');
+ }
+ })
+ .catch((err) => {
+ console.log(err);
+ router.replace('/login');
+ });
+ }
+ }, [params, router, setUser]);
+
+ return null;
+}
diff --git a/src/app/(auth)/(callback)/naver/oauth/page.tsx b/src/app/(auth)/(callback)/naver/oauth/page.tsx
new file mode 100644
index 00000000..06102058
--- /dev/null
+++ b/src/app/(auth)/(callback)/naver/oauth/page.tsx
@@ -0,0 +1,16 @@
+import { Suspense } from 'react';
+import NaverCallbackClient from './NaverCallback';
+
+export default function Page() {
+ return (
+
+
+
+ }
+ >
+
+
+ );
+}
diff --git a/src/app/(auth)/login/page.tsx b/src/app/(auth)/login/page.tsx
new file mode 100644
index 00000000..7d513d66
--- /dev/null
+++ b/src/app/(auth)/login/page.tsx
@@ -0,0 +1,24 @@
+import LoginSocialList from '@/components/login/LoginSocialList';
+import Image from 'next/image';
+
+export default function Home() {
+ return (
+
+
+
+
+
+ ํจ๊ป์ ์์์ ๋ถ๋๋ฝ๊ฒ.
+
+ ์์ฐ์ค๋ฌ์ด ์ฐ๊ฒฐ๋ก ์์๋๋ ํ๋น๋ฉ
+
+
+
+ );
+}
diff --git a/src/app/(main)/layout.tsx b/src/app/(main)/layout.tsx
new file mode 100644
index 00000000..f5ef1dbb
--- /dev/null
+++ b/src/app/(main)/layout.tsx
@@ -0,0 +1,17 @@
+import Footer from '@/components/common/Footer';
+import Header from '@/components/common/Header';
+import MobileOverlayWrapper from '@/components/layout/MobileOverlayWrapper';
+
+export default function MainLayout({ children }: { children: React.ReactNode }) {
+ return (
+ <>
+
+
+ {children}
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/(main)/mypage/_components/MyPageInfo.tsx b/src/app/(main)/mypage/_components/MyPageInfo.tsx
new file mode 100644
index 00000000..aee300d0
--- /dev/null
+++ b/src/app/(main)/mypage/_components/MyPageInfo.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import MyPageTitle from './MyPageTitle';
+import EmptyApplicantStatus from './applicant/EmptyApplicantStatus';
+import EmptyAppliedTeam from './applied/EmptyAppliedTeam';
+import InitialProfile from './profile/InitialProfile';
+import ProfileSlider from './profile/ProfileSlider';
+import AppliedTeamSection from './applied/AppliedTeamsSection';
+import ApplicantStatusSection from './applicant/ApplicantStatusSection';
+import { useGetProfileList } from '@/hooks/queries/useProfile';
+import { useDashboard } from '@/hooks/queries/useDashboard';
+import EmptyMyTeam from './myteam/EmptyMyTeam';
+import MyTeamSection from './myteam/MyTeamSection';
+
+const MyPageInfo = () => {
+ const { data: profiles } = useGetProfileList();
+ const hasProfileData = profiles && profiles.length > 0;
+
+ const { data: dashboard } = useDashboard();
+ const myApplications = dashboard?.myApplications ?? [];
+ const myRecruitingPost = dashboard?.myRecruitingPost ?? [];
+ const myTeamResponses = dashboard?.myTeamResponses ?? [];
+
+ const hasApplications = myApplications.length > 0;
+ const hasRecruitingPosts = myRecruitingPost.length > 0;
+ const hasMyTeamResponses = myTeamResponses.length > 0;
+
+ return (
+
+
+ {hasProfileData ?
:
}
+
+ {hasApplications ? (
+
+ ) : (
+
+ )}
+
+ {hasRecruitingPosts ? (
+
+ ) : (
+
+ )}
+
+ {hasMyTeamResponses ?
:
}
+
+ );
+};
+
+export default MyPageInfo;
diff --git a/src/app/(main)/mypage/_components/MyPageTitle.tsx b/src/app/(main)/mypage/_components/MyPageTitle.tsx
new file mode 100644
index 00000000..9696de8e
--- /dev/null
+++ b/src/app/(main)/mypage/_components/MyPageTitle.tsx
@@ -0,0 +1,23 @@
+import Link from 'next/link';
+
+const MyPageTitle = () => {
+ return (
+
+
+
๋ง์ดํ์ด์ง
+
+ ๋ํ ํค์๋ ์์ ์{' '}
+
+ ํํผ์
๋ก
+ {' '}
+ ํ์ด์ง์์ ๊ฐ๋ฅํฉ๋๋ค
+
+
+
+ ํ๋กํ ๊ด๋ฆฌํ๊ธฐ
+
+
+ );
+};
+
+export default MyPageTitle;
diff --git a/src/app/(main)/mypage/_components/applicant/ApplicantStatusCard.tsx b/src/app/(main)/mypage/_components/applicant/ApplicantStatusCard.tsx
new file mode 100644
index 00000000..230ebf97
--- /dev/null
+++ b/src/app/(main)/mypage/_components/applicant/ApplicantStatusCard.tsx
@@ -0,0 +1,48 @@
+import { Tag } from '@/components/common/Tag';
+import { MyRecruitingPost } from '@/types/project';
+import { formatDday } from '@/utils/project/formatDate';
+import { useRouter } from 'next/navigation';
+
+interface ApplicantStatusCardProps {
+ recruiting: MyRecruitingPost;
+}
+
+const ApplicantStatusCard = ({ recruiting }: ApplicantStatusCardProps) => {
+ const router = useRouter();
+
+ const handleClick = () => {
+ if (recruiting.recruitingPostStatus === '๋ชจ์ง ๋ง๊ฐ') {
+ router.push(`/project/${recruiting.recruitingPostId}`);
+ } else {
+ router.push(`/project/${recruiting.recruitingPostId}/applicants`);
+ }
+ };
+
+ return (
+
+
+
+ ํ์ฌ {recruiting.totalApplicants}๋ช
์ด ์ง์ํ์ด์
+
+
+ {recruiting.recruitingPostStatus === '๋ชจ์ง ๋ง๊ฐ' ? '๋ง๊ฐ' : formatDday(recruiting.dday)}
+
+
+
+ {recruiting.title}
+
+
+ {recruiting.tags.map((tag) => (
+
+ #{tag}
+
+ ))}
+
+
+ );
+};
+
+export default ApplicantStatusCard;
diff --git a/src/app/(main)/mypage/_components/applicant/ApplicantStatusSection.tsx b/src/app/(main)/mypage/_components/applicant/ApplicantStatusSection.tsx
new file mode 100644
index 00000000..2bf2ea0a
--- /dev/null
+++ b/src/app/(main)/mypage/_components/applicant/ApplicantStatusSection.tsx
@@ -0,0 +1,91 @@
+'use client';
+
+import { MyRecruitingPost } from '@/types/project';
+import ApplicantStatusCard from './ApplicantStatusCard';
+import { useRouter } from 'next/navigation';
+import Image from 'next/image';
+import { useSwipeableCards } from '@/hooks/useSwipeableCards';
+
+interface ApplicantStatusSectionProps {
+ recruitings: MyRecruitingPost[];
+}
+
+const ApplicantStatusSection = ({ recruitings }: ApplicantStatusSectionProps) => {
+ const router = useRouter();
+ const {
+ currentIndex,
+ setCurrentIndex,
+ displayItems: displayApplicants,
+ onTouchStart,
+ onTouchMove,
+ onTouchEnd,
+ } = useSwipeableCards({ items: recruitings });
+
+ const handleClick = () => {
+ router.push('/mypage/applicant');
+ };
+
+ if (displayApplicants.length === 0) return null;
+
+ return (
+ <>
+
+
์ง์์ ํํฉ
+
+
+
+ {recruitings.map((post) => (
+
+ ))}
+
+
+
+
+ {displayApplicants.map((app) => (
+
+ ))}
+
+
+ {displayApplicants.length > 1 && (
+
+ {displayApplicants.map((_, i) => (
+
+ )}
+
+ >
+ );
+};
+
+export default ApplicantStatusSection;
diff --git a/src/app/(main)/mypage/_components/applicant/EmptyApplicantStatus.tsx b/src/app/(main)/mypage/_components/applicant/EmptyApplicantStatus.tsx
new file mode 100644
index 00000000..9b6c8bcf
--- /dev/null
+++ b/src/app/(main)/mypage/_components/applicant/EmptyApplicantStatus.tsx
@@ -0,0 +1,40 @@
+import Link from 'next/link';
+
+const EmptyApplicantStatus = () => {
+ return (
+ <>
+
+
์ง์์ ํํฉ
+
+ ์ ์ฒด๋ณด๊ธฐ
+
+
+
+
+
+ ํ์ ๋ชจ์งํ๊ธฐ
+ {' '}
+ ํ์ด์ง๋ก ์ด๋ํด์
+
+ ํจ๊ปํ ํ๋ก์ ํธ ํ์์ ๋ชจ์งํด๋ณด์ธ์!
+
+
+
+
+
+ ํ์ ๋ชจ์งํ๊ธฐ
+ {' '}
+ ํ์ด์ง๋ก
+ ์ด๋ํด์ ํจ๊ปํ ํ๋ก์ ํธ
+
+ ํ์์ ๋ชจ์งํด๋ณด์ธ์!
+
+
+ >
+ );
+};
+
+export default EmptyApplicantStatus;
diff --git a/src/app/(main)/mypage/_components/applied/AppliedTeamCard.tsx b/src/app/(main)/mypage/_components/applied/AppliedTeamCard.tsx
new file mode 100644
index 00000000..e725f4ad
--- /dev/null
+++ b/src/app/(main)/mypage/_components/applied/AppliedTeamCard.tsx
@@ -0,0 +1,62 @@
+import { Tag } from '@/components/common/Tag';
+import { MyApplication } from '@/types/project';
+import Image from 'next/image';
+import { formatDateDot } from '@/utils/project/formatDate';
+import { useRouter } from 'next/navigation';
+import { getStatusColor } from '@/utils/project/getStatusColor';
+
+interface AppliedTeamCardProps {
+ application: MyApplication;
+}
+
+const AppliedTeamCard = ({ application }: AppliedTeamCardProps) => {
+ const router = useRouter();
+
+ const handleClick = () => {
+ router.push(`/project/${application.recruitingPostId}`);
+ };
+
+ const profileSrc = application.profileImage ? application.profileImage : '/icons/profile.svg';
+
+ return (
+
+
+
+ {application.period}
+ {application.progressWay}
+
+
{application.status}
+
+
+ {application.title}
+
+
+ {application.tags.map((tag) => (
+
+ #{tag}
+
+ ))}
+
+
+
+
+ {application.writerName}
+
+
+ {formatDateDot(application.deadline)}
+
+
+
+ );
+};
+
+export default AppliedTeamCard;
diff --git a/src/app/(main)/mypage/_components/applied/AppliedTeamsSection.tsx b/src/app/(main)/mypage/_components/applied/AppliedTeamsSection.tsx
new file mode 100644
index 00000000..2805a1a0
--- /dev/null
+++ b/src/app/(main)/mypage/_components/applied/AppliedTeamsSection.tsx
@@ -0,0 +1,91 @@
+'use client';
+
+import { MyApplication } from '@/types/project';
+import AppliedTeamCard from './AppliedTeamCard';
+import { useRouter } from 'next/navigation';
+import Image from 'next/image';
+import { useSwipeableCards } from '@/hooks/useSwipeableCards';
+
+interface AppliedTeamSectionProps {
+ applications: MyApplication[];
+}
+
+const AppliedTeamSection = ({ applications }: AppliedTeamSectionProps) => {
+ const router = useRouter();
+ const {
+ currentIndex,
+ setCurrentIndex,
+ displayItems: displayApplications,
+ onTouchStart,
+ onTouchMove,
+ onTouchEnd,
+ } = useSwipeableCards({ items: applications });
+
+ const handleClick = () => {
+ router.push('/mypage/applied');
+ };
+
+ if (applications.length === 0) return null;
+
+ return (
+ <>
+
+
๋ด๊ฐ ์ง์ํ ํ
+
+
+
+ {applications.map((app) => (
+
+ ))}
+
+
+
+
+ {displayApplications.map((app) => (
+
+ ))}
+
+
+ {displayApplications.length > 1 && (
+
+ {displayApplications.map((_, i) => (
+
+ )}
+
+ >
+ );
+};
+
+export default AppliedTeamSection;
diff --git a/src/app/(main)/mypage/_components/applied/EmptyAppliedTeam.tsx b/src/app/(main)/mypage/_components/applied/EmptyAppliedTeam.tsx
new file mode 100644
index 00000000..fa7bcd7b
--- /dev/null
+++ b/src/app/(main)/mypage/_components/applied/EmptyAppliedTeam.tsx
@@ -0,0 +1,37 @@
+import Link from 'next/link';
+
+const EmptyAppliedTeam = () => {
+ return (
+ <>
+
+
๋ด๊ฐ ์ง์ํ ํ
+
+ ์ ์ฒด๋ณด๊ธฐ
+
+
+
+
+
+ ํ๋ก์ ํธ
+ {' '}
+ ํ์ด์ง๋ก ์ด๋ํด์
+
+ ๋ด๊ฐ ๊ฐ์ง ์ํํธ ์คํฌ๋ก ํ์ ์ง์ํด๋ณด์ธ์!
+
+
+
+
+
+ ํ๋ก์ ํธ
+ {' '}
+ ํ์ด์ง๋ก ์ด๋ํด์
+
+ ๋ด๊ฐ ๊ฐ์ง ์ํํธ ์คํฌ๋ก
+ ํ์ ์ง์ํด๋ณด์ธ์!
+
+
+ >
+ );
+};
+
+export default EmptyAppliedTeam;
diff --git a/src/app/(main)/mypage/_components/myteam/EmptyMyTeam.tsx b/src/app/(main)/mypage/_components/myteam/EmptyMyTeam.tsx
new file mode 100644
index 00000000..62b6bbc3
--- /dev/null
+++ b/src/app/(main)/mypage/_components/myteam/EmptyMyTeam.tsx
@@ -0,0 +1,32 @@
+import Link from 'next/link';
+
+const EmptyMyTeam = () => {
+ return (
+ <>
+
+
์ฐธ์ฌ์ค์ธ ํ
+
+ ์ ์ฒด๋ณด๊ธฐ
+
+
+
+
+ ๋๊ฐ ํจ๊ปํ๊ฒ ๋ ๊น์?
+
+ ํ๋ก์ ํธ์ ์ง์ํด ํ์ ๋ค์ด๊ฐ๋ณด์ธ์!
+
+
+
+
+ ๋๊ฐ ํจ๊ปํ๊ฒ ๋ ๊น์?
+
+ ํ๋ก์ ํธ์ ์ง์ํด ํ์
+
+ ๋ค์ด๊ฐ๋ณด์ธ์!
+
+
+ >
+ );
+};
+
+export default EmptyMyTeam;
diff --git a/src/app/(main)/mypage/_components/myteam/MyTeamCard.tsx b/src/app/(main)/mypage/_components/myteam/MyTeamCard.tsx
new file mode 100644
index 00000000..6b23065c
--- /dev/null
+++ b/src/app/(main)/mypage/_components/myteam/MyTeamCard.tsx
@@ -0,0 +1,42 @@
+'use client';
+
+import { Tag } from '@/components/common/Tag';
+import { MyTeamResponses } from '@/types/project';
+import { useRouter } from 'next/navigation';
+
+interface MyTeamCardProps {
+ myteam: MyTeamResponses;
+}
+
+const MyTeamCard = ({ myteam }: MyTeamCardProps) => {
+ const router = useRouter();
+
+ const handleClick = () => {
+ router.push(`/mypage/team/${myteam.postId}`);
+ };
+
+ return (
+
+
+
+ {myteam.period}
+ {myteam.progressWay}
+
+
{myteam.totalMembers}๋ช
์ฐธ์ฌ
+
+
{myteam.title}
+
+ {myteam.tags.map((tag) => (
+
+ #{tag}
+
+ ))}
+
+
+ );
+};
+
+export default MyTeamCard;
diff --git a/src/app/(main)/mypage/_components/myteam/MyTeamSection.tsx b/src/app/(main)/mypage/_components/myteam/MyTeamSection.tsx
new file mode 100644
index 00000000..008a7638
--- /dev/null
+++ b/src/app/(main)/mypage/_components/myteam/MyTeamSection.tsx
@@ -0,0 +1,93 @@
+'use client';
+
+import { useRouter } from 'next/navigation';
+import MyTeamCard from './MyTeamCard';
+import { MyTeamResponses } from '@/types/project';
+import Image from 'next/image';
+import { useSwipeableCards } from '@/hooks/useSwipeableCards';
+
+interface MyTeamSectionProps {
+ teams: MyTeamResponses[];
+}
+
+const MyTeamSection = ({ teams }: MyTeamSectionProps) => {
+ const router = useRouter();
+ const {
+ currentIndex,
+ setCurrentIndex,
+ displayItems: displayTeams,
+ onTouchStart,
+ onTouchMove,
+ onTouchEnd,
+ } = useSwipeableCards({ items: teams });
+
+ const handleClick = () => {
+ router.push('/mypage/team');
+ };
+
+ if (teams.length === 0) return null;
+
+ return (
+ <>
+
+
์ฐธ์ฌ์ค์ธ ํ
+
+
+
+
+ {teams.map((team) => (
+
+ ))}
+
+
+
+
+
+ {displayTeams.map((team) => (
+
+
+
+ ))}
+
+
+ {displayTeams.length > 1 && (
+
+ {displayTeams.map((_, i) => (
+
+ )}
+
+ >
+ );
+};
+
+export default MyTeamSection;
diff --git a/src/app/(main)/mypage/_components/profile/InitialProfile.tsx b/src/app/(main)/mypage/_components/profile/InitialProfile.tsx
new file mode 100644
index 00000000..460ab594
--- /dev/null
+++ b/src/app/(main)/mypage/_components/profile/InitialProfile.tsx
@@ -0,0 +1,65 @@
+'use client';
+
+import Button from '@/components/common/button/Button';
+import { useUserStore } from '@/store/useUserStore';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+
+const InitialProfile = () => {
+ const { userName } = useUserStore();
+ const router = useRouter();
+
+ if (!userName) {
+ return null;
+ }
+
+ return (
+
+
+
+
+
+
{userName}๋
+
+
์ฐ๋ฝ์๋จ
+
+
์ฐ๋ฝ์๋จ์ ๋ฑ๋กํด์ฃผ์ธ์
+
+
+
์์
์๊ฐ
+
+
์์
์๊ฐ๋๋ฅผ ์ ํํด์ฃผ์ธ์
+
+
+
+
+
+ {Array(3)
+ .fill(null)
+ .map((_, i) => (
+
+ ))}
+
+
+ );
+};
+
+export default InitialProfile;
diff --git a/src/app/(main)/mypage/_components/profile/ProfileCard.tsx b/src/app/(main)/mypage/_components/profile/ProfileCard.tsx
new file mode 100644
index 00000000..4a7ed9f0
--- /dev/null
+++ b/src/app/(main)/mypage/_components/profile/ProfileCard.tsx
@@ -0,0 +1,138 @@
+import Button from '@/components/common/button/Button';
+import { ResponseProfile } from '@/types/profile';
+import Image from 'next/image';
+import ProfileLinkButton from './ProfileLinkButton';
+import { useRouter } from 'next/navigation';
+import Link from 'next/link';
+import { useModal } from '@/contexts/ModalContext';
+
+interface ProfileCardProps {
+ profile: ResponseProfile;
+}
+
+const ProfileCard = ({ profile }: ProfileCardProps) => {
+ const router = useRouter();
+
+ const { openModal } = useModal();
+
+ const handleOpenDetailModal = () => {
+ openModal('profileDetail', { profile: profile });
+ };
+
+ return (
+ <>
+
+
+
+
+
+ {profile.userName}๋
+
+ {profile.headKeywords.length > 0 ? (
+
+ {profile.headKeywords.map((keyword) => (
+
+ #{keyword}
+
+ ))}
+
+ ) : (
+
+ )}
+
+
+
+
+
+ ์ ์ฒด ํค์๋ ๋ณด๊ธฐ
+
+
+
+
+
+
+
+
+
{profile.userName}๋
+
+
์ฐ๋ฝ์๋จ
+
+
+ {profile.contactWay ?? '์ฐ๋ฝ์๋จ์ ๋ฑ๋กํด์ฃผ์ธ์'}
+
+
+
+
์์
์๊ฐ
+
+
+ {profile.workingTime
+ ? `${profile.workingTime}์ ์์
ํ๋๊ฒ ํธํด์`
+ : '์์
์๊ฐ๋๋ฅผ ์ ํํด์ฃผ์ธ์'}
+
+
+ {profile.headKeywords.length > 0 ? (
+
+ {profile.headKeywords.map((keyword) => (
+
+ #{keyword}
+
+ ))}
+
+ ) : (
+
+ )}
+
+
+
+
+
+ {profile.links &&
+ profile.links.length > 0 &&
+ profile.links.map((link, index) =>
)}
+
+
+ {profile.headKeywords.length > 0 && (
+
+ )}
+
+
+ >
+ );
+};
+
+export default ProfileCard;
diff --git a/src/app/(main)/mypage/_components/profile/ProfileLinkButton.tsx b/src/app/(main)/mypage/_components/profile/ProfileLinkButton.tsx
new file mode 100644
index 00000000..3ff0d56b
--- /dev/null
+++ b/src/app/(main)/mypage/_components/profile/ProfileLinkButton.tsx
@@ -0,0 +1,43 @@
+'use client';
+import { getLinkType } from '@/utils/project/linkType';
+import Image from 'next/image';
+import React from 'react';
+
+const ProfileLinkButton = ({ link }: { link: string }) => {
+ const type = getLinkType(link);
+
+ const isValidUrl = (url: string) => {
+ try {
+ const parsed = new URL(url);
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
+ } catch {
+ return false;
+ }
+ };
+
+ if (!isValidUrl(link)) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export default ProfileLinkButton;
diff --git a/src/app/(main)/mypage/_components/profile/ProfileSlider.tsx b/src/app/(main)/mypage/_components/profile/ProfileSlider.tsx
new file mode 100644
index 00000000..f27cd450
--- /dev/null
+++ b/src/app/(main)/mypage/_components/profile/ProfileSlider.tsx
@@ -0,0 +1,93 @@
+'use client';
+
+import Image from 'next/image';
+import { useState } from 'react';
+import ProfileCard from './ProfileCard';
+import { useGetProfileList } from '@/hooks/queries/useProfile';
+import { ResponseProfile } from '@/types/profile';
+import { useRouter } from 'next/navigation';
+
+const ProfileSlider = () => {
+ const router = useRouter();
+ const [index, setIndex] = useState(0);
+ const { data: profiles } = useGetProfileList();
+
+ if (!profiles || profiles.length === 0) return null;
+
+ const currentProfile: ResponseProfile = profiles[index];
+
+ const handlePrev = () => {
+ if (index > 0) setIndex(index - 1);
+ };
+
+ const handleNext = () => {
+ if (index < profiles.length - 1) setIndex(index + 1);
+ };
+
+ const handleClickManageProfile = () => {
+ router.push('/mypage/profile');
+ };
+
+ return (
+ <>
+
+
+
+
+
+ {index > 0 ? (
+
+ ) : (
+
+ )}
+
+ {index < profiles.length - 1 ? (
+
+ ) : (
+
+ )}
+
+
+ {profiles.map((_, i) => (
+
+ >
+ );
+};
+
+export default ProfileSlider;
diff --git a/src/app/(main)/mypage/applicant/_components/ApplicantTeamCard.tsx b/src/app/(main)/mypage/applicant/_components/ApplicantTeamCard.tsx
new file mode 100644
index 00000000..4016d394
--- /dev/null
+++ b/src/app/(main)/mypage/applicant/_components/ApplicantTeamCard.tsx
@@ -0,0 +1,73 @@
+'use client';
+
+import { Tag } from '@/components/common/Tag';
+import { CurrentApplicant } from '@/types/applicant';
+import { formatDday } from '@/utils/project/formatDate';
+import { useRouter } from 'next/navigation';
+
+interface ApplicantTeamCardProps {
+ application: CurrentApplicant;
+ index: number;
+}
+
+const ApplicantTeamCard = ({ application, index }: ApplicantTeamCardProps) => {
+ const router = useRouter();
+
+ const handleClick = () => {
+ if (application.recruitingPostStatus === '๋ชจ์ง ๋ง๊ฐ') {
+ router.push(`/project/${application.recruitingPostId}`);
+ } else {
+ router.push(`/project/${application.recruitingPostId}/applicants`);
+ }
+ };
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default ApplicantTeamCard;
diff --git a/src/app/(main)/mypage/applicant/_components/ApplicantTeamList.tsx b/src/app/(main)/mypage/applicant/_components/ApplicantTeamList.tsx
new file mode 100644
index 00000000..7a77cdab
--- /dev/null
+++ b/src/app/(main)/mypage/applicant/_components/ApplicantTeamList.tsx
@@ -0,0 +1,106 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import Pagination from '@/components/common/Pagination';
+import ApplicantTeamCard from './ApplicantTeamCard';
+import { useCurrentApplicants } from '@/hooks/queries/useApplicant';
+import { CurrentApplicant } from '@/types/applicant';
+import Button from '@/components/common/button/Button';
+
+interface ApplicantTeamListProps {
+ status: string;
+ page: number;
+ setPage: (page: number) => void;
+}
+
+const ApplicantTeamList = ({ status, page, setPage }: ApplicantTeamListProps) => {
+ const [mobilePage, setMobilePage] = useState(1);
+ const [allApplicants, setAllApplicants] = useState([]);
+ const [hasMore, setHasMore] = useState(true);
+ const [currentStatus, setCurrentStatus] = useState(status);
+
+ const { data } = useCurrentApplicants(status, page - 1, 6);
+ const { data: mobileData, isLoading: isMobileLoading } = useCurrentApplicants(
+ status,
+ mobilePage - 1,
+ 20,
+ );
+
+ const applicants = data?.content ?? [];
+ const totalPages = data?.totalPages ?? 1;
+
+ useEffect(() => {
+ if (currentStatus !== status) {
+ setMobilePage(1);
+ setHasMore(true);
+ setCurrentStatus(status);
+ }
+ }, [status, currentStatus]);
+
+ useEffect(() => {
+ if (mobileData) {
+ const newApplicants = mobileData.content ?? [];
+ if (mobilePage === 1) {
+ setAllApplicants(newApplicants);
+ } else {
+ setAllApplicants((prev) => [...prev, ...newApplicants]);
+ }
+ setHasMore(mobilePage < (mobileData.totalPages ?? 1));
+ }
+ }, [mobileData, mobilePage]);
+
+ const handleLoadMore = () => {
+ if (hasMore && !isMobileLoading) {
+ setMobilePage((prev) => prev + 1);
+ }
+ };
+
+ return (
+ <>
+
+ {allApplicants.length > 0 ? (
+ <>
+ {allApplicants.map((app, idx) => (
+
+ ))}
+ {hasMore && !isMobileLoading && (
+
+ )}
+ >
+ ) : isMobileLoading ? (
+
๋ก๋ฉ ์ค...
+ ) : (
+
๋ชจ์ง ํํฉ์ด ์์ต๋๋ค.
+ )}
+
+
+
+
์๋ฒ
+
์ ๋ชฉ ๋ฐ ๋ด์ฉ
+
๊ณต๊ณ ๋ง๊ฐ์ผ
+
+
+ {applicants.length > 0 ? (
+ applicants.map((app, idx) => (
+
+ ))
+ ) : (
+
๋ชจ์ง ํํฉ์ด ์์ต๋๋ค.
+ )}
+
+
+
+ >
+ );
+};
+
+export default ApplicantTeamList;
diff --git a/src/app/(main)/mypage/applicant/_components/ApplicantTitle.tsx b/src/app/(main)/mypage/applicant/_components/ApplicantTitle.tsx
new file mode 100644
index 00000000..83317844
--- /dev/null
+++ b/src/app/(main)/mypage/applicant/_components/ApplicantTitle.tsx
@@ -0,0 +1,28 @@
+'use client';
+
+import DropdownSmall from '@/components/common/DropdownSmall';
+import { APPLICANT_STATUS } from '@/constants/Dropdown';
+
+interface ApplicantTitleProps {
+ applicantStatus: string;
+ setApplicantStatus: (v: string) => void;
+}
+
+const ApplicantTitle = ({ applicantStatus, setApplicantStatus }: ApplicantTitleProps) => {
+ return (
+ <>
+
+
์ง์์ ํํฉ
+
setApplicantStatus(v)}
+ options={APPLICANT_STATUS}
+ />
+
+ >
+ );
+};
+
+export default ApplicantTitle;
diff --git a/src/app/(main)/mypage/applicant/page.tsx b/src/app/(main)/mypage/applicant/page.tsx
new file mode 100644
index 00000000..c987b8c6
--- /dev/null
+++ b/src/app/(main)/mypage/applicant/page.tsx
@@ -0,0 +1,24 @@
+'use client';
+
+import { useState } from 'react';
+import ApplicantTeamList from './_components/ApplicantTeamList';
+import ApplicantTitle from './_components/ApplicantTitle';
+import MobileHeader from '@/components/common/MobileHeader';
+
+const Page = () => {
+ const [status, setStatus] = useState('');
+ const [page, setPage] = useState(1);
+
+ return (
+ <>
+
+
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/mypage/applied/_components/AppliedTeamCard.tsx b/src/app/(main)/mypage/applied/_components/AppliedTeamCard.tsx
new file mode 100644
index 00000000..5acc785b
--- /dev/null
+++ b/src/app/(main)/mypage/applied/_components/AppliedTeamCard.tsx
@@ -0,0 +1,67 @@
+'use client';
+
+import { Tag } from '@/components/common/Tag';
+import { Application } from '@/types/applicant';
+import { formatDateDot } from '@/utils/project/formatDate';
+import { getStatusColor } from '@/utils/project/getStatusColor';
+import { useRouter } from 'next/navigation';
+
+interface AppliedTeamCardProps {
+ applicant: Application;
+ index: number;
+}
+
+const AppliedTeamCard = ({ applicant, index }: AppliedTeamCardProps) => {
+ const router = useRouter();
+
+ const handleClick = () => {
+ router.push(`/project/${applicant.recruitingPostId}`);
+ };
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default AppliedTeamCard;
diff --git a/src/app/(main)/mypage/applied/_components/AppliedTeamList.tsx b/src/app/(main)/mypage/applied/_components/AppliedTeamList.tsx
new file mode 100644
index 00000000..7ac4189c
--- /dev/null
+++ b/src/app/(main)/mypage/applied/_components/AppliedTeamList.tsx
@@ -0,0 +1,107 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import Pagination from '@/components/common/Pagination';
+import AppliedTeamCard from './AppliedTeamCard';
+import { useMyApplications } from '@/hooks/queries/useApplicant';
+import { Application } from '@/types/applicant';
+import Button from '@/components/common/button/Button';
+
+interface AppliedTeamListProps {
+ status: string;
+ page: number;
+ setPage: (page: number) => void;
+}
+
+const AppliedTeamList = ({ status, page, setPage }: AppliedTeamListProps) => {
+ const [mobilePage, setMobilePage] = useState(1);
+ const [allApplications, setAllApplications] = useState([]);
+ const [hasMore, setHasMore] = useState(true);
+ const [currentStatus, setCurrentStatus] = useState(status);
+
+ const { data } = useMyApplications(status, page - 1, 6);
+ const { data: mobileData, isLoading: isMobileLoading } = useMyApplications(
+ status,
+ mobilePage - 1,
+ 20,
+ );
+
+ const applications = data?.content ?? [];
+ const totalPages = data?.totalPages ?? 1;
+
+ useEffect(() => {
+ if (currentStatus !== status) {
+ setMobilePage(1);
+ setHasMore(true);
+ setCurrentStatus(status);
+ }
+ }, [status, currentStatus]);
+
+ useEffect(() => {
+ if (mobileData) {
+ const newApplications = mobileData.content ?? [];
+ if (mobilePage === 1) {
+ setAllApplications(newApplications);
+ } else {
+ setAllApplications((prev) => [...prev, ...newApplications]);
+ }
+ setHasMore(mobilePage < (mobileData.totalPages ?? 1));
+ }
+ }, [mobileData, mobilePage]);
+
+ const handleLoadMore = () => {
+ if (hasMore && !isMobileLoading) {
+ setMobilePage((prev) => prev + 1);
+ }
+ };
+
+ return (
+ <>
+
+ {allApplications.length > 0 ? (
+ <>
+ {allApplications.map((app, idx) => (
+
+ ))}
+ {hasMore && !isMobileLoading && (
+
+ )}
+ >
+ ) : isMobileLoading ? (
+
๋ก๋ฉ ์ค...
+ ) : (
+
์ง์ ๋ด์ญ์ด ์์ต๋๋ค.
+ )}
+
+
+
+
์๋ฒ
+
์ ๋ชฉ ๋ฐ ๋ด์ฉ
+
์ง์ํํฉ
+
์ง์๋ ์ง
+
+
+ {applications.length > 0 ? (
+ applications.map((app, idx) => (
+
+ ))
+ ) : (
+
์ง์ ๋ด์ญ์ด ์์ต๋๋ค.
+ )}
+
+
+
+ >
+ );
+};
+
+export default AppliedTeamList;
diff --git a/src/app/(main)/mypage/applied/_components/AppliedTitle.tsx b/src/app/(main)/mypage/applied/_components/AppliedTitle.tsx
new file mode 100644
index 00000000..5d5504ce
--- /dev/null
+++ b/src/app/(main)/mypage/applied/_components/AppliedTitle.tsx
@@ -0,0 +1,26 @@
+'use client';
+
+import DropdownSmall from '@/components/common/DropdownSmall';
+import { APPLIED_TEAMS } from '@/constants/Dropdown';
+
+interface AppliedTitleProps {
+ applicantStatus: string;
+ setApplicantStatus: (v: string) => void;
+}
+
+const AppliedTitle = ({ applicantStatus, setApplicantStatus }: AppliedTitleProps) => {
+ return (
+
+
๋ด๊ฐ ์ง์ํ ํ
+
setApplicantStatus(v)}
+ options={APPLIED_TEAMS}
+ />
+
+ );
+};
+
+export default AppliedTitle;
diff --git a/src/app/(main)/mypage/applied/page.tsx b/src/app/(main)/mypage/applied/page.tsx
new file mode 100644
index 00000000..ab32a3e2
--- /dev/null
+++ b/src/app/(main)/mypage/applied/page.tsx
@@ -0,0 +1,23 @@
+'use client';
+
+import { useState } from 'react';
+import AppliedTeamList from './_components/AppliedTeamList';
+import AppliedTitle from './_components/AppliedTitle';
+import MobileHeader from '@/components/common/MobileHeader';
+
+const Page = () => {
+ const [status, setStatus] = useState('');
+ const [page, setPage] = useState(1);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/mypage/page.tsx b/src/app/(main)/mypage/page.tsx
new file mode 100644
index 00000000..3894b1ff
--- /dev/null
+++ b/src/app/(main)/mypage/page.tsx
@@ -0,0 +1,11 @@
+import MyPageInfo from './_components/MyPageInfo';
+
+const Page = () => {
+ return (
+ <>
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/mypage/profile/_components/ProfileCard.tsx b/src/app/(main)/mypage/profile/_components/ProfileCard.tsx
new file mode 100644
index 00000000..4c3535cb
--- /dev/null
+++ b/src/app/(main)/mypage/profile/_components/ProfileCard.tsx
@@ -0,0 +1,109 @@
+import Button from '@/components/common/button/Button';
+import { ResponseProfile } from '@/types/profile';
+import { useRouter } from 'next/navigation';
+import ProfileHeader from './ProfileHeader';
+import ProfileInfo from './ProfileInfo';
+import ProfileLinks from './ProfileLinks';
+import { useModal } from '@/contexts/ModalContext';
+import Link from 'next/link';
+import Image from 'next/image';
+
+interface ProfileCardProps {
+ profile?: ResponseProfile;
+}
+
+const ProfileCard = ({ profile }: ProfileCardProps) => {
+ const router = useRouter();
+ const { openModal } = useModal();
+
+ const handleEdit = () => {
+ if (!profile) return;
+ router.push(`/mypage/profile/edit/${profile?.profileId}`);
+ };
+
+ const handleKeywords = () => {
+ router.push('/teampsylog');
+ };
+
+ const handleDelete = () => {
+ if (!profile) return;
+ openModal('profileDelete', {
+ profileId: profile.profileId,
+ profileName: profile.profileName,
+ });
+ };
+
+ const handleClickDetail = () => {
+ if (!profile) return;
+ openModal('profileDetail', { profile });
+ };
+
+ return (
+
+
+
+
+
+
+
+ {profile?.userName}๋
+
+
+ {profile?.headKeywords && profile?.headKeywords.length > 0 ? (
+
+ {profile?.headKeywords.map((keyword) => (
+
+ #{keyword}
+
+ ))}
+
+ ) : (
+
+ )}
+
+
+
+
+
+
+ ์ ์ฒด ํค์๋ ๋ณด๊ธฐ
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ProfileCard;
diff --git a/src/app/(main)/mypage/profile/_components/ProfileHeader.tsx b/src/app/(main)/mypage/profile/_components/ProfileHeader.tsx
new file mode 100644
index 00000000..71fa30e9
--- /dev/null
+++ b/src/app/(main)/mypage/profile/_components/ProfileHeader.tsx
@@ -0,0 +1,26 @@
+interface ProfileHeaderProps {
+ title?: string;
+ handleEdit: () => void;
+ handleDelete: () => void;
+}
+
+const ProfileHeader = ({ title, handleEdit, handleDelete }: ProfileHeaderProps) => (
+
+
{title || '์ ํ๋กํ'}
+
+
+
+
+
+
+
+
+);
+
+export default ProfileHeader;
diff --git a/src/app/(main)/mypage/profile/_components/ProfileInfo.tsx b/src/app/(main)/mypage/profile/_components/ProfileInfo.tsx
new file mode 100644
index 00000000..e02d7c5e
--- /dev/null
+++ b/src/app/(main)/mypage/profile/_components/ProfileInfo.tsx
@@ -0,0 +1,48 @@
+import { useUserStore } from '@/store/useUserStore';
+import { ResponseProfile } from '@/types/profile';
+import Image from 'next/image';
+import ProfileKeywords from './ProfileKeywords';
+
+interface ProfileInfoProps {
+ profile?: ResponseProfile;
+}
+
+const ProfileInfo = ({ profile }: ProfileInfoProps) => {
+ const { userName } = useUserStore();
+
+ return (
+
+
+
+
{profile?.userName || userName}
+
+
+
์ฐ๋ฝ์๋จ
+
+
+ {profile?.contactWay || '์ฐ๋ฝ์๋จ์ ๋ฑ๋กํด์ฃผ์ธ์'}
+
+
+
+
+
์์
์๊ฐ
+
+
+ {profile?.workingTime
+ ? `${profile.workingTime}์ ์์
ํ๋๊ฒ ํธํด์`
+ : '์์
์๊ฐ๋๋ฅผ ์ ํํด์ฃผ์ธ์'}
+
+
+
+
+
+ );
+};
+
+export default ProfileInfo;
diff --git a/src/app/(main)/mypage/profile/_components/ProfileKeywords.tsx b/src/app/(main)/mypage/profile/_components/ProfileKeywords.tsx
new file mode 100644
index 00000000..d59f07dd
--- /dev/null
+++ b/src/app/(main)/mypage/profile/_components/ProfileKeywords.tsx
@@ -0,0 +1,22 @@
+import { Keyword } from '@/components/common/Tag';
+
+interface ProfileKeywordsProps {
+ keywords?: string[];
+}
+
+const ProfileKeywords = ({ keywords }: ProfileKeywordsProps) => {
+ const defaultKeywords = ['๋ํํค์๋1', '๋ํํค์๋2', '๋ํํค์๋3'];
+ const displayKeywords = keywords && keywords.length > 0 ? keywords : defaultKeywords;
+
+ return (
+
+ {displayKeywords.map((k, i) => (
+
+ #{k}
+
+ ))}
+
+ );
+};
+
+export default ProfileKeywords;
diff --git a/src/app/(main)/mypage/profile/_components/ProfileLinks.tsx b/src/app/(main)/mypage/profile/_components/ProfileLinks.tsx
new file mode 100644
index 00000000..faefc84e
--- /dev/null
+++ b/src/app/(main)/mypage/profile/_components/ProfileLinks.tsx
@@ -0,0 +1,40 @@
+import Image from 'next/image';
+import { getLinkType } from '@/utils/project/linkType';
+
+interface ProfileLinksProps {
+ links?: string[] | null;
+}
+
+const iconMap = {
+ behance: '/icons/behance.svg',
+ github: '/icons/github.svg',
+ notion: '/icons/notion.svg',
+ linkedin: '/icons/linkedin.svg',
+ other: '/icons/etclink.svg',
+} as const;
+
+const ProfileLinks = ({ links }: ProfileLinksProps) => {
+ const validLinks = (links || []).filter((l) => l && l.trim() !== '');
+
+ const displayLinks = validLinks.length > 0 ? validLinks : ['', '', ''];
+
+ return (
+
+ {displayLinks.map((link, i) => {
+ if (link) {
+ const type = getLinkType(link);
+ return (
+
+
+
+ );
+ }
+ return (
+
+ );
+ })}
+
+ );
+};
+
+export default ProfileLinks;
diff --git a/src/app/(main)/mypage/profile/_components/ProfileList.tsx b/src/app/(main)/mypage/profile/_components/ProfileList.tsx
new file mode 100644
index 00000000..d3600695
--- /dev/null
+++ b/src/app/(main)/mypage/profile/_components/ProfileList.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import Image from 'next/image';
+import ProfileCard from './ProfileCard';
+import { useGetProfileList } from '@/hooks/queries/useProfile';
+import { useCreateProfile } from '@/hooks/mutation/useCreateProfile';
+
+const ProfileList = () => {
+ const { data: profiles } = useGetProfileList();
+ const { mutate: createProfile } = useCreateProfile();
+ const hasProfiles = profiles && profiles.length > 0;
+
+ const handleAddProfile = () => {
+ if ((profiles?.length ?? 0) >= 3) return;
+
+ createProfile({
+ profileName: null,
+ workingTime: null,
+ links: [''],
+ contactWay: null,
+ });
+ };
+
+ return (
+ <>
+
+
+ {hasProfiles ? (
+ profiles.map((profile) =>
)
+ ) : (
+
์์ง ๋ฑ๋ก๋ ํ๋กํ์ด ์์ต๋๋ค.
+ )}
+
+ {(!hasProfiles || profiles.length < 3) && (
+
+ )}
+
+ >
+ );
+};
+
+export default ProfileList;
diff --git a/src/app/(main)/mypage/profile/_components/ProfileTitle.tsx b/src/app/(main)/mypage/profile/_components/ProfileTitle.tsx
new file mode 100644
index 00000000..c8db087e
--- /dev/null
+++ b/src/app/(main)/mypage/profile/_components/ProfileTitle.tsx
@@ -0,0 +1,19 @@
+import Link from 'next/link';
+import React from 'react';
+
+const ProfileTitle = () => {
+ return (
+
+
ํ๋กํ ๊ด๋ฆฌํ๊ธฐ
+
+ ๋ํ ํค์๋ ์์ ์{' '}
+
+ ํํผ์
๋ก
+ {' '}
+ ํ์ด์ง์์ ๊ฐ๋ฅํฉ๋๋ค
+
+
+ );
+};
+
+export default ProfileTitle;
diff --git a/src/app/(main)/mypage/profile/edit/[id]/_components/LabeledTextarea.tsx b/src/app/(main)/mypage/profile/edit/[id]/_components/LabeledTextarea.tsx
new file mode 100644
index 00000000..e1c4f2ec
--- /dev/null
+++ b/src/app/(main)/mypage/profile/edit/[id]/_components/LabeledTextarea.tsx
@@ -0,0 +1,51 @@
+'use client';
+
+interface LabeledTextareaProps {
+ id: string;
+ label: string;
+ placeholder: string;
+ iconSrc?: string;
+ className?: string;
+ value: string;
+ onChange: (e: React.ChangeEvent) => void;
+}
+
+import Image from 'next/image';
+import clsx from 'clsx';
+
+const LabeledTextarea = ({
+ id,
+ label,
+ placeholder,
+ iconSrc,
+ className,
+ value,
+ onChange,
+}: LabeledTextareaProps) => {
+ return (
+
+
{label}
+
+ {iconSrc && (
+
+ )}
+
+
+
+ );
+};
+
+export default LabeledTextarea;
diff --git a/src/app/(main)/mypage/profile/edit/[id]/_components/ProfileEditContainer.tsx b/src/app/(main)/mypage/profile/edit/[id]/_components/ProfileEditContainer.tsx
new file mode 100644
index 00000000..ea347b3f
--- /dev/null
+++ b/src/app/(main)/mypage/profile/edit/[id]/_components/ProfileEditContainer.tsx
@@ -0,0 +1,203 @@
+'use client';
+
+import WorkTimeDropdown from './WorkingTimeDropdown';
+import Button from '@/components/common/button/Button';
+import LabeledTextarea from './LabeledTextarea';
+import { useEffect, useState } from 'react';
+import { ResponseProfile } from '@/types/profile';
+import { useUpdateProfile } from '@/hooks/mutation/useUpdateProfile';
+import { useModal } from '@/contexts/ModalContext';
+import { useToast } from '@/contexts/ToastContext';
+import Image from 'next/image';
+
+interface ProfileEditContainerProps {
+ profile: ResponseProfile;
+}
+
+const MAX_LINKS = 3;
+
+const ProfileEditContainer = ({ profile }: ProfileEditContainerProps) => {
+ const { openModal } = useModal();
+ const { addToast } = useToast();
+
+ const [formData, setFormData] = useState({
+ title: '',
+ contact: '',
+ workingTime: '',
+ links: Array(MAX_LINKS).fill(''),
+ });
+
+ useEffect(() => {
+ if (profile) {
+ const links = [...(profile.links || [])];
+ while (links.length < MAX_LINKS) links.push('');
+ setFormData({
+ title: profile.profileName || '',
+ contact: profile.contactWay || '',
+ workingTime: profile.workingTime || '',
+ links,
+ });
+ }
+ }, [profile]);
+
+ const handleDeleteProfile = () => {
+ openModal('profileDelete', {
+ profileId: profile.profileId,
+ profileName: profile.profileName,
+ });
+ };
+
+ const handleChange =
+ (key: keyof typeof formData, index?: number) => (e: React.ChangeEvent) => {
+ if (key === 'links' && typeof index === 'number') {
+ const updatedLinks = [...formData.links];
+ updatedLinks[index] = e.target.value;
+ setFormData({ ...formData, links: updatedLinks });
+ } else {
+ setFormData({ ...formData, [key]: e.target.value });
+ }
+ };
+
+ const { mutate: updateProfile } = useUpdateProfile();
+
+ const handleSubmit = () => {
+ const cleanedLinks =
+ formData.links.length > 0
+ ? formData.links.map((l) => (l.trim() === '' ? '' : l.trim()))
+ : [''];
+
+ const finalLinks = cleanedLinks.every((l) => l === '') ? [''] : cleanedLinks;
+
+ updateProfile(
+ {
+ profileId: profile.profileId,
+ profileName: formData.title.trim() === '' ? null : formData.title,
+ workingTime: formData.workingTime.trim() === '' ? null : formData.workingTime,
+ contactWay: formData.contact.trim() === '' ? null : formData.contact,
+ links: finalLinks,
+ },
+ {
+ onSuccess: () => {
+ openModal('profileEditComplete');
+ },
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ onError: (error: any) => {
+ const code = error.response?.data?.code;
+
+ switch (code) {
+ case 'PROFILE4008':
+ addToast({
+ type: 'error',
+ title: 'ํด๋น ํ๋กํ๋ก ์ง์ ์ค์ธ ๊ณต๊ณ ๊ฐ ์์ด ์์ ํ ์ ์์ด์',
+ message: '๋ชจ์ง ๊ธฐํ๋์์ ํ๋กํ ์์ ์ด ์ ํ๋ผ์',
+ });
+ break;
+
+ case 'PROFILE4009':
+ addToast({
+ type: 'error',
+ title: 'ํด๋น ํ๋กํ๋ก ๋ชจ์ง ์ค์ธ ์์ฑ๊ธ์ด ์์ด ์์ ํ ์ ์์ด์',
+ message: '๋ชจ์ง ๊ธฐํ๋์์ ํ๋กํ ์์ ์ด ์ ํ๋ผ์',
+ });
+ break;
+
+ case 'PROFILE4010':
+ addToast({
+ type: 'error',
+ title: 'ํด๋น ํ๋กํ๋ก ์ง์์ค์ธ ๊ณต๊ณ ๋ ๋ชจ์ง ์ค์ธ ์์ฑ๊ธ์ด ์์ด ์์ ํ ์ ์์ด์',
+ message: '๋ชจ์ง ๊ธฐํ๋์์ ํ๋กํ ์์ ์ด ์ ํ๋ผ์',
+ });
+ break;
+
+ default:
+ addToast({
+ type: 'error',
+ title: 'ํ๋กํ ์์ ์คํจ',
+ message: '์์ ์ค ์ ์ ์๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.',
+ });
+ }
+ },
+ },
+ );
+ };
+
+ return (
+
+
+
+
+
+
+
์์
์๊ฐ
+
setFormData({ ...formData, workingTime: value })}
+ />
+
+
+
+
+
๋๋ฅผ ์๊ฐํ๋ ๋งํฌ ์ถ๊ฐํ๊ธฐ
+
๋งํฌ๋ ์ต๋ 3๊ฐ๊น์ง ์
๋ ฅํ ์ ์์ด์
+
+ {formData.links.map((link, idx) => (
+
+ ))}
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ProfileEditContainer;
diff --git a/src/app/(main)/mypage/profile/edit/[id]/_components/ProfileImage.tsx b/src/app/(main)/mypage/profile/edit/[id]/_components/ProfileImage.tsx
new file mode 100644
index 00000000..d5db5409
--- /dev/null
+++ b/src/app/(main)/mypage/profile/edit/[id]/_components/ProfileImage.tsx
@@ -0,0 +1,134 @@
+'use client';
+
+import Button from '@/components/common/button/Button';
+import { useUploadProfileImage } from '@/hooks/mutation/useUploadProfileImage';
+import { useDeleteProfileImage } from '@/hooks/mutation/useDeleteProfileImage';
+import { useUserStore } from '@/store/useUserStore';
+import { ResponseProfile } from '@/types/profile';
+import Image from 'next/image';
+import { useState } from 'react';
+
+interface ProfileImageProps {
+ profile: ResponseProfile;
+}
+
+const ProfileImage = ({ profile }: ProfileImageProps) => {
+ const { userName } = useUserStore();
+ const { mutate: uploadProfileImage } = useUploadProfileImage();
+ const { mutate: deleteProfileImage } = useDeleteProfileImage();
+ const [isModalOpen, setIsModalOpen] = useState(false);
+
+ const handleImageChange = () => {
+ const input = document.createElement('input');
+ input.type = 'file';
+ input.accept = 'image/*';
+
+ input.onchange = (e) => {
+ const file = (e.target as HTMLInputElement).files?.[0];
+ if (!file || !profile) return;
+
+ uploadProfileImage(
+ { profileId: profile.profileId, file },
+ {
+ onSuccess: () => {
+ console.log('์ด๋ฏธ์ง ์
๋ก๋ ์ฑ๊ณต');
+ setIsModalOpen(false);
+ },
+ onError: (error) => {
+ console.error('์ด๋ฏธ์ง ์
๋ก๋ ์คํจ:', error);
+ alert('์ด๋ฏธ์ง ์
๋ก๋์ ์คํจํ์ต๋๋ค.');
+ },
+ },
+ );
+ };
+
+ input.click();
+ };
+
+ const handleUpload = () => {
+ setIsModalOpen(false);
+ handleImageChange();
+ };
+
+ const handleDelete = () => {
+ if (!profile) return;
+
+ deleteProfileImage(profile.profileId, {
+ onSuccess: () => {
+ setIsModalOpen(false);
+ },
+ onError: (error) => {
+ console.error('์ด๋ฏธ์ง ์ญ์ ์คํจ:', error);
+ alert('์ด๋ฏธ์ง ์ญ์ ์ ์คํจํ์ต๋๋ค.');
+ },
+ });
+ };
+
+ const handleModalToggle = () => {
+ setIsModalOpen(!isModalOpen);
+ };
+
+ return (
+ <>
+
+ {isModalOpen && (
+ <>
+
+
+
+
+
+
+ >
+ )}
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ProfileImage;
diff --git a/src/app/(main)/mypage/profile/edit/[id]/_components/WorkingTimeDropdown.tsx b/src/app/(main)/mypage/profile/edit/[id]/_components/WorkingTimeDropdown.tsx
new file mode 100644
index 00000000..1258635a
--- /dev/null
+++ b/src/app/(main)/mypage/profile/edit/[id]/_components/WorkingTimeDropdown.tsx
@@ -0,0 +1,79 @@
+'use client';
+
+import { useMemo, useRef, useState } from 'react';
+import Image from 'next/image';
+import clsx from 'clsx';
+import { WORKING_TIME_OPTIONS, WORKING_VALUE_MAP } from '@/constants/Dropdown';
+import useOutsideClick from '@/hooks/useOutsideClick';
+
+interface WorkTimeDropdownProps {
+ value: string;
+ onChange: (value: string) => void;
+}
+
+const WorkTimeDropdown = ({ value, onChange }: WorkTimeDropdownProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+
+ useOutsideClick(dropdownRef, () => setIsOpen(false));
+
+ const normalizedValue = useMemo(() => {
+ if (!value) return '';
+ if (Object.values(WORKING_VALUE_MAP).includes(value)) return value;
+ return WORKING_VALUE_MAP[value] || '';
+ }, [value]);
+
+ const handleSelect = (option: string) => {
+ onChange(option);
+ setIsOpen(false);
+ };
+
+ const selectedOption = WORKING_TIME_OPTIONS.find((opt) => opt.value === normalizedValue);
+ const selectedLabel = selectedOption ? selectedOption.label : '';
+
+ return (
+
+
+
+ {isOpen && (
+
+ {WORKING_TIME_OPTIONS.map((option, idx) => (
+ - handleSelect(option.value)}
+ className={clsx(
+ 'hover:bg-primary-50 tablet:px-7 cursor-pointer px-5 py-3 text-gray-800',
+ value === option.value && 'bg-primary-50',
+ idx !== WORKING_TIME_OPTIONS.length - 1 && 'border-b border-gray-300',
+ )}
+ >
+ {option.label}
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default WorkTimeDropdown;
diff --git a/src/app/(main)/mypage/profile/edit/[id]/page.tsx b/src/app/(main)/mypage/profile/edit/[id]/page.tsx
new file mode 100644
index 00000000..a9d86c6b
--- /dev/null
+++ b/src/app/(main)/mypage/profile/edit/[id]/page.tsx
@@ -0,0 +1,33 @@
+'use client';
+
+import { useParams } from 'next/navigation';
+import ProfileTitle from '../../_components/ProfileTitle';
+import ProfileEditContainer from './_components/ProfileEditContainer';
+import ProfileImage from './_components/ProfileImage';
+import { useGetProfile } from '@/hooks/queries/useProfile';
+import MobileHeader from '@/components/common/MobileHeader';
+
+const Page = () => {
+ const { id } = useParams();
+ const profileId = Array.isArray(id) ? Number(id[0]) : Number(id);
+ const { data: profile } = useGetProfile({ profileId });
+ if (isNaN(profileId)) {
+ return ์ ํจํ์ง ์์ ํ๋กํ ID์
๋๋ค.
;
+ }
+ if (!profile) {
+ return ํ๋กํ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ์ค...
;
+ }
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/mypage/profile/page.tsx b/src/app/(main)/mypage/profile/page.tsx
new file mode 100644
index 00000000..8a7ba34c
--- /dev/null
+++ b/src/app/(main)/mypage/profile/page.tsx
@@ -0,0 +1,17 @@
+import MobileHeader from '@/components/common/MobileHeader';
+import ProfileList from './_components/ProfileList';
+import ProfileTitle from './_components/ProfileTitle';
+
+const Page = () => {
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/mypage/team/[id]/_components/MemberInfo.tsx b/src/app/(main)/mypage/team/[id]/_components/MemberInfo.tsx
new file mode 100644
index 00000000..a7757acb
--- /dev/null
+++ b/src/app/(main)/mypage/team/[id]/_components/MemberInfo.tsx
@@ -0,0 +1,48 @@
+import { Keyword } from '@/components/common/Tag';
+import { WORKING_TIME_KR } from '@/constants/Translate';
+import { ResponseConfirmedProfile } from '@/types/myteam';
+import { getPositionLabel } from '@/utils/project/positionLabel';
+
+const MemberInfo = ({ member }: { member: ResponseConfirmedProfile }) => {
+ return (
+
+
+
{member.userName}
+
+ {getPositionLabel(member.position)}
+
+
+
+
+
์ฐ๋ฝ์๋จ
+
+
+ {member.contactWay || '์ฐ๋ฝ์๋จ์ ๋ฑ๋กํ์ง ์์์ต๋๋ค'}
+
+
+
+
+
์์
์๊ฐ
+
+
+ {member.workingTime
+ ? `${WORKING_TIME_KR[member.workingTime]}์ ์์
ํ๋๊ฒ ํธํด์`
+ : '์์
์๊ฐ๋๋ฅผ ์ ํํ์ง ์์์ต๋๋ค'}
+
+
+
+
+ {member.keywords.map((k, i) => (
+
+ #{k}
+
+ ))}
+
+
+ );
+};
+
+export default MemberInfo;
diff --git a/src/app/(main)/mypage/team/[id]/_components/MemberLinks.tsx b/src/app/(main)/mypage/team/[id]/_components/MemberLinks.tsx
new file mode 100644
index 00000000..1a9c6929
--- /dev/null
+++ b/src/app/(main)/mypage/team/[id]/_components/MemberLinks.tsx
@@ -0,0 +1,43 @@
+import Image from 'next/image';
+import { getLinkType } from '@/utils/project/linkType';
+import { isValidUrl } from '@/utils/project/validateUrl';
+
+const iconMap = {
+ behance: '/icons/behance.svg',
+ github: '/icons/github.svg',
+ notion: '/icons/notion.svg',
+ linkedin: '/icons/linkedin.svg',
+ other: '/icons/etclink.svg',
+} as const;
+
+const MemberLinks = ({ links }: { links: string[] }) => {
+ return (
+
+ {Array.from({ length: 3 }).map((_, i) => {
+ const linkIndex = links.length - 3 + i;
+ const link = links[linkIndex];
+
+ if (link && isValidUrl(link)) {
+ const type = getLinkType(link);
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+ })}
+
+ );
+};
+
+export default MemberLinks;
diff --git a/src/app/(main)/mypage/team/[id]/_components/MemberProfile.tsx b/src/app/(main)/mypage/team/[id]/_components/MemberProfile.tsx
new file mode 100644
index 00000000..178da29b
--- /dev/null
+++ b/src/app/(main)/mypage/team/[id]/_components/MemberProfile.tsx
@@ -0,0 +1,16 @@
+import { ResponseConfirmedProfile } from '@/types/myteam';
+import Image from 'next/image';
+
+const MemberProfile = ({ member }: { member: ResponseConfirmedProfile }) => {
+ return (
+
+ );
+};
+
+export default MemberProfile;
diff --git a/src/app/(main)/mypage/team/[id]/_components/TeamMemberItem.tsx b/src/app/(main)/mypage/team/[id]/_components/TeamMemberItem.tsx
new file mode 100644
index 00000000..7442523d
--- /dev/null
+++ b/src/app/(main)/mypage/team/[id]/_components/TeamMemberItem.tsx
@@ -0,0 +1,100 @@
+import { ResponseConfirmedProfile } from '@/types/myteam';
+import { ResponseProfile } from '@/types/profile';
+import { WORKING_TIME_KR } from '@/constants/Translate';
+import MemberProfile from './MemberProfile';
+import MemberInfo from './MemberInfo';
+import MemberLinks from './MemberLinks';
+import Button from '@/components/common/button/Button';
+import Image from 'next/image';
+import Link from 'next/link';
+import { useRouter } from 'next/navigation';
+import { useModal } from '@/contexts/ModalContext';
+
+interface TeamMemberItemProps {
+ member: ResponseConfirmedProfile;
+}
+
+const TeamMemberItem = ({ member }: TeamMemberItemProps) => {
+ const router = useRouter();
+ const { openModal } = useModal();
+
+ const handleOpenDetailModal = () => {
+ // ResponseConfirmedProfile์ ResponseProfile ํํ๋ก ๋ณํ
+ const profile: ResponseProfile = {
+ profileId: 0,
+ userId: 0,
+ userName: member.userName,
+ profileImageUrl: member.profileImage,
+ profileName: member.profileName,
+ position: member.position,
+ workingTime: member.workingTime
+ ? WORKING_TIME_KR[member.workingTime] || member.workingTime
+ : '',
+ links: member.links,
+ contactWay: member.contactWay || '',
+ headKeywords: member.keywords,
+ createdAt: '',
+ modifiedAt: '',
+ uuid: member.uuid || '',
+ };
+ openModal('profileDetail', { profile });
+ };
+
+ return (
+ <>
+
+
+
+
+
+ {member.userName}๋
+
+ {member.keywords.length > 0 ? (
+
+ {member.keywords.map((keyword) => (
+
+ #{keyword}
+
+ ))}
+
+ ) : (
+
+ )}
+
+
+
+
+
+ ์ ์ฒด ํค์๋ ๋ณด๊ธฐ
+
+
+
+
+
+ >
+ );
+};
+
+export default TeamMemberItem;
diff --git a/src/app/(main)/mypage/team/[id]/_components/TeamMemberList.tsx b/src/app/(main)/mypage/team/[id]/_components/TeamMemberList.tsx
new file mode 100644
index 00000000..0dc0b548
--- /dev/null
+++ b/src/app/(main)/mypage/team/[id]/_components/TeamMemberList.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import ErrorDisplay from '@/components/common/Error';
+import Loading from '@/components/common/Loading';
+import { useConfirmedProfiles } from '@/hooks/queries/useConfirmedProfiles';
+import TeamMemberItem from './TeamMemberItem';
+
+interface TeamMemberListProps {
+ postId: number;
+ position: string;
+}
+
+const TeamMemberList = ({ postId, position }: TeamMemberListProps) => {
+ const { data, isLoading, error } = useConfirmedProfiles(postId, position);
+
+ if (isLoading) return ;
+ if (error) return ;
+ if (!data || data.length === 0) return ํ์์ด ์์ต๋๋ค
;
+
+ return (
+
+ {data.map((member, index) => (
+
+ ))}
+
+ );
+};
+
+export default TeamMemberList;
diff --git a/src/app/(main)/mypage/team/[id]/_components/TeamTitle.tsx b/src/app/(main)/mypage/team/[id]/_components/TeamTitle.tsx
new file mode 100644
index 00000000..fdd48317
--- /dev/null
+++ b/src/app/(main)/mypage/team/[id]/_components/TeamTitle.tsx
@@ -0,0 +1,40 @@
+'use client';
+
+import DropdownSmall from '@/components/common/DropdownSmall';
+import { MY_TEAM_DROPDOWN } from '@/constants/Dropdown';
+
+interface MyTeamTitleProps {
+ teamMemberStatus: string;
+ setTeamMemberStatus: (v: string) => void;
+}
+
+const MyTeamTitle = ({ teamMemberStatus, setTeamMemberStatus }: MyTeamTitleProps) => {
+ return (
+ <>
+
+ setTeamMemberStatus(v)}
+ options={MY_TEAM_DROPDOWN}
+ />
+
+
+
์ฐธ์ฌ์ค์ธ ํ
+
+ {`โํํผ์
ํ์๊ตฌํด์' ๋ชจ์ง๊ธ์ ํตํด ๊ตฌ์ฑ๋ ํ์์
๋๋ค`}
+
+
setTeamMemberStatus(v)}
+ options={MY_TEAM_DROPDOWN}
+ />
+
+ >
+ );
+};
+
+export default MyTeamTitle;
diff --git a/src/app/(main)/mypage/team/[id]/page.tsx b/src/app/(main)/mypage/team/[id]/page.tsx
new file mode 100644
index 00000000..666121eb
--- /dev/null
+++ b/src/app/(main)/mypage/team/[id]/page.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import { useState } from 'react';
+import TeamMemberList from './_components/TeamMemberList';
+import TeamTitle from './_components/TeamTitle';
+import { useParams } from 'next/navigation';
+import ErrorDisplay from '@/components/common/Error';
+import MobileHeader from '@/components/common/MobileHeader';
+
+const Page = () => {
+ const [status, setStatus] = useState('');
+ const { id } = useParams();
+ const postId = Array.isArray(id) ? Number(id[0]) : Number(id);
+ if (isNaN(postId)) {
+ return ;
+ }
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/mypage/team/_components/MyTeamCard.tsx b/src/app/(main)/mypage/team/_components/MyTeamCard.tsx
new file mode 100644
index 00000000..79c97304
--- /dev/null
+++ b/src/app/(main)/mypage/team/_components/MyTeamCard.tsx
@@ -0,0 +1,64 @@
+'use client';
+
+import { Tag } from '@/components/common/Tag';
+import { MyTeamItem } from '@/types/myteam';
+import { useRouter } from 'next/navigation';
+
+interface MyTeamCardProps {
+ team: MyTeamItem;
+ index: number;
+}
+
+const MyTeamCard = ({ team, index }: MyTeamCardProps) => {
+ const router = useRouter();
+
+ const handleClick = () => {
+ router.push(`/mypage/team/${team.postId}`);
+ };
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default MyTeamCard;
diff --git a/src/app/(main)/mypage/team/_components/MyTeamList.tsx b/src/app/(main)/mypage/team/_components/MyTeamList.tsx
new file mode 100644
index 00000000..ed19f63d
--- /dev/null
+++ b/src/app/(main)/mypage/team/_components/MyTeamList.tsx
@@ -0,0 +1,88 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import Pagination from '@/components/common/Pagination';
+import MyTeamCard from './MyTeamCard';
+import { useMyTeams } from '@/hooks/queries/useMyTeams';
+import { MyTeamItem } from '@/types/myteam';
+import Button from '@/components/common/button/Button';
+
+interface MyTeamListProps {
+ page: number;
+ setPage: (page: number) => void;
+}
+
+const MyTeamList = ({ page, setPage }: MyTeamListProps) => {
+ const [mobilePage, setMobilePage] = useState(1);
+ const [allTeams, setAllTeams] = useState([]);
+ const [hasMore, setHasMore] = useState(true);
+
+ const { data } = useMyTeams(page - 1, 6);
+ const { data: mobileData, isLoading: isMobileLoading } = useMyTeams(mobilePage - 1, 20);
+
+ const teams = data?.content ?? [];
+ const totalPages = data?.totalPages ?? 1;
+
+ useEffect(() => {
+ if (mobileData) {
+ const newTeams = mobileData.content ?? [];
+ if (mobilePage === 1) {
+ setAllTeams(newTeams);
+ } else {
+ setAllTeams((prev) => [...prev, ...newTeams]);
+ }
+ setHasMore(mobilePage < (mobileData.totalPages ?? 1));
+ }
+ }, [mobileData, mobilePage]);
+
+ const handleLoadMore = () => {
+ if (hasMore && !isMobileLoading) {
+ setMobilePage((prev) => prev + 1);
+ }
+ };
+
+ return (
+ <>
+
+ {allTeams.length > 0 ? (
+ <>
+ {allTeams.map((team, idx) => (
+
+ ))}
+ {hasMore && !isMobileLoading && (
+
+ )}
+ >
+ ) : isMobileLoading ? (
+
๋ก๋ฉ ์ค...
+ ) : (
+
์ฐธ์ฌ์ค์ธ ํ์ด ์์ต๋๋ค.
+ )}
+
+
+
+
์๋ฒ
+
์ ๋ชฉ ๋ฐ ๋ด์ฉ
+
ํ์ ์
+
+
+ {teams.length > 0 ? (
+ teams.map((item, idx) => (
+
+ ))
+ ) : (
+
์ฐธ์ฌ์ค์ธ ํ์ด ์์ต๋๋ค.
+ )}
+
+
+
+ >
+ );
+};
+
+export default MyTeamList;
diff --git a/src/app/(main)/mypage/team/_components/MyTeamTitle.tsx b/src/app/(main)/mypage/team/_components/MyTeamTitle.tsx
new file mode 100644
index 00000000..9ba035d6
--- /dev/null
+++ b/src/app/(main)/mypage/team/_components/MyTeamTitle.tsx
@@ -0,0 +1,10 @@
+const MyTeamTitle = () => {
+ return (
+
+
์ฐธ์ฌ์ค์ธ ํ
+
๋งค์นญ ์ฑ๊ณต๋์ด ํ๋ก์ ํธ๊ฐ ์์๋ ํ ๋ชฉ๋ก์
๋๋ค
+
+ );
+};
+
+export default MyTeamTitle;
diff --git a/src/app/(main)/mypage/team/page.tsx b/src/app/(main)/mypage/team/page.tsx
new file mode 100644
index 00000000..a3d3c3dc
--- /dev/null
+++ b/src/app/(main)/mypage/team/page.tsx
@@ -0,0 +1,22 @@
+'use client';
+
+import { useState } from 'react';
+import MyTeamTitle from './_components/MyTeamTitle';
+import MyTeamList from './_components/MyTeamList';
+import MobileHeader from '@/components/common/MobileHeader';
+
+const Page = () => {
+ const [page, setPage] = useState(1);
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx
new file mode 100644
index 00000000..373935ff
--- /dev/null
+++ b/src/app/(main)/page.tsx
@@ -0,0 +1,5 @@
+import { redirect } from 'next/navigation';
+
+export default function RootPage() {
+ redirect('/project');
+}
diff --git a/src/app/(main)/project/[id]/_components/DDayTag.tsx b/src/app/(main)/project/[id]/_components/DDayTag.tsx
new file mode 100644
index 00000000..29904bf3
--- /dev/null
+++ b/src/app/(main)/project/[id]/_components/DDayTag.tsx
@@ -0,0 +1,28 @@
+'use client';
+
+import { Status, StatusType } from '@/utils/project';
+import mapStatusToEng from '@/utils/project/mapStatustoEng';
+
+type DDayProps = {
+ status: StatusType | string;
+ dday: number;
+};
+
+const DDay = ({ status, dday }: DDayProps) => {
+ const engStatus = mapStatusToEng(status);
+
+ const getText = () => {
+ if (engStatus === Status.OPEN) {
+ return dday > 0 ? `D-${dday}` : 'D-DAY';
+ }
+ return '๋ง๊ฐ';
+ };
+
+ return (
+
+ {getText()}
+
+ );
+};
+
+export default DDay;
diff --git a/src/app/(main)/project/[id]/_components/InfoCard.tsx b/src/app/(main)/project/[id]/_components/InfoCard.tsx
new file mode 100644
index 00000000..5bda6ad3
--- /dev/null
+++ b/src/app/(main)/project/[id]/_components/InfoCard.tsx
@@ -0,0 +1,33 @@
+import React from 'react';
+import InfoItem from './InfoItem';
+import { ResponseProject } from '@/types/project';
+import { formatDate } from '@/utils/project/formatDate';
+import { formatPositions } from '@/utils/project/formatPositions';
+import { PERIOD_KR, PROGRESS_WAY_KR } from '@/constants/Translate';
+
+const InfoCard = ({
+ startDate,
+ period,
+ progressWay,
+ contactWay,
+ recruitingPositions,
+}: ResponseProject) => {
+ const formatPosition = formatPositions(recruitingPositions);
+ const formatStartDate = formatDate(startDate);
+
+ return (
+
+ );
+};
+
+export default InfoCard;
diff --git a/src/app/(main)/project/[id]/_components/InfoItem.tsx b/src/app/(main)/project/[id]/_components/InfoItem.tsx
new file mode 100644
index 00000000..f3d06f70
--- /dev/null
+++ b/src/app/(main)/project/[id]/_components/InfoItem.tsx
@@ -0,0 +1,36 @@
+import React from 'react';
+
+type InfoItemProps = {
+ label: string;
+ value: string;
+ className?: string;
+};
+
+const InfoItem = ({ label, value, className = '' }: InfoItemProps) => {
+ const isUrl = /^(https?:\/\/|www\.)/i.test(value);
+
+ return (
+
+
{label}
+
+ {isUrl ? (
+
+ {value}
+
+ ) : (
+
+ {value}
+
+ )}
+
+
+ );
+};
+
+export default InfoItem;
diff --git a/src/app/(main)/project/[id]/_components/ProjectInfo.tsx b/src/app/(main)/project/[id]/_components/ProjectInfo.tsx
new file mode 100644
index 00000000..e11e10dd
--- /dev/null
+++ b/src/app/(main)/project/[id]/_components/ProjectInfo.tsx
@@ -0,0 +1,38 @@
+'use client';
+import DOMPurify from 'isomorphic-dompurify';
+import React from 'react';
+import ProjectTitle from './ProjectTitle';
+import InfoCard from './InfoCard';
+import Profile1 from '@/components/profile/Profile1';
+import { useGetProject } from '@/hooks/queries/useProject';
+
+const ProjectInfo = ({ id }: { id: string }) => {
+ const { data } = useGetProject(
+ { postId: Number(id) },
+ { refetchOnMount: true, refetchOnWindowFocus: true },
+ );
+ if (!data) return null;
+
+ const sanitizedContent = DOMPurify.sanitize(data.content.replace(/<\/p>/g, '
'));
+ const isWriter = data.writer;
+
+ return (
+
+ );
+};
+
+export default ProjectInfo;
diff --git a/src/app/(main)/project/[id]/_components/ProjectTitle.tsx b/src/app/(main)/project/[id]/_components/ProjectTitle.tsx
new file mode 100644
index 00000000..a89b173d
--- /dev/null
+++ b/src/app/(main)/project/[id]/_components/ProjectTitle.tsx
@@ -0,0 +1,142 @@
+'use client';
+
+import Button from '@/components/common/Button';
+import { useModal } from '@/contexts/ModalContext';
+import { ResponseProject } from '@/types/project';
+import React, { useState } from 'react';
+import DDay from './DDayTag';
+import Link from 'next/link';
+import { useUserStore } from '@/store/useUserStore';
+import { useRouter } from 'next/navigation';
+import Image from 'next/image';
+import { useMediaQuery } from '@/hooks/useMediaQuery';
+
+const ProjectTitle = ({
+ postId,
+ title,
+ status,
+ dday,
+ createdAt,
+ deadline,
+ recruitingPositions,
+ alreadyApplied,
+ writer,
+}: ResponseProject) => {
+ const { openModal } = useModal();
+ const { userName } = useUserStore();
+ const isLoggedIn = !!userName;
+ const navigate = useRouter();
+
+ const isDesktop = useMediaQuery('(min-width: 1024px)');
+
+ const handleApplyModal = () => {
+ if (!isLoggedIn) {
+ window.alert('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.');
+ navigate.push('/login');
+ } else {
+ const modalType = isDesktop ? 'apply' : 'applyMo';
+ openModal(modalType, { postId, recruitingPositions });
+ }
+ };
+
+ const handleDeleteModal = () => {
+ openModal('delete', { postId: postId, projectName: title });
+ };
+
+ const [isMoreOpen, setIsMoreOpen] = useState(false);
+
+ const handleMobileMore = () => {
+ setIsMoreOpen(!isMoreOpen);
+ };
+
+ return (
+
+
+
+
+ {createdAt.split(' ')[0]}~{deadline}
+
+
+ {status === 'OPEN' ? (
+ writer ? (
+ <>
+
+
+
+
+
+
+
+
+ {isMoreOpen && (
+ <>
+
+ {/* ํ๋จ ๋ฒํผ ์์ญ */}
+
+
+
+
+
+
+
+ >
+ )}
+ >
+ ) : (
+ <>
+
+ {alreadyApplied ? (
+
+ ) : (
+
+ )}
+
+
+ {alreadyApplied ? (
+
+ ) : (
+
+ )}
+
+ >
+ )
+ ) : null}
+
+ );
+};
+
+export default ProjectTitle;
diff --git a/src/app/(main)/project/[id]/applicants/_components/CurrentApplicantItem.tsx b/src/app/(main)/project/[id]/applicants/_components/CurrentApplicantItem.tsx
new file mode 100644
index 00000000..bda5c225
--- /dev/null
+++ b/src/app/(main)/project/[id]/applicants/_components/CurrentApplicantItem.tsx
@@ -0,0 +1,72 @@
+import { Tag } from '@/components/common/Tag';
+import { CurrentApplicant } from '@/types/application';
+import Image from 'next/image';
+import React from 'react';
+import clsx from 'clsx';
+import ProfileTag from '@/components/profile/ProfileTag';
+
+const CurrentApplicantItem = ({
+ item,
+ onClick,
+ cardStyle,
+}: {
+ item: CurrentApplicant;
+ onClick: () => void;
+ cardStyle?: string;
+}) => {
+ const isSelected = item.applicationStatus === '๋งค์นญ์ฑ๊ณต';
+
+ const deCardStyle = isSelected
+ ? 'desktop:flex hidden flex-col items-center gap-4 rounded-2xl border-[2px] border-primary-900 bg-primary-50 p-10'
+ : 'desktop:flex hidden cursor-pointer flex-col items-center gap-4 rounded-2xl border border-gray-300 p-10';
+
+ const moCardStyle = isSelected
+ ? 'desktop:hidden flex flex-col w-full gap-1.5 rounded-2xl border-[2px] border-primary-900 bg-primary-50 p-4'
+ : 'desktop:hidden flex cursor-pointer flex-col w-full gap-1.5 rounded-2xl border border-gray-300 p-4';
+
+ const tagStyle = isSelected ? 'bg-primary-900 text-gray-0' : 'bg-gray-200 text-gray-700';
+
+ if (cardStyle === 'desktop') {
+ return (
+
+
+
+ {item.profilePosition}
+ {item.applicantName}
+
+
+ );
+ }
+
+ // mobile
+ return (
+
+
+
+
+
{item.applicantName}
+
+
{item.profilePosition}
+
+
+ {(item.keywordList || []).map((keyword, index) => (
+
{keyword}
+ ))}
+
+
+ );
+};
+
+export default CurrentApplicantItem;
diff --git a/src/app/(main)/project/[id]/applicants/_components/CurrentApplicants.tsx b/src/app/(main)/project/[id]/applicants/_components/CurrentApplicants.tsx
new file mode 100644
index 00000000..8c2f1fd6
--- /dev/null
+++ b/src/app/(main)/project/[id]/applicants/_components/CurrentApplicants.tsx
@@ -0,0 +1,98 @@
+import React, { useState, useEffect } from 'react';
+import DropdownSmall from '@/components/common/DropdownSmall';
+import { RECRUIT_OPTIONS } from '@/constants/Dropdown';
+import { PositionType } from '@/utils/position';
+import { POSITION_KR } from '@/constants/Translate';
+import CurrentApplicantItem from './CurrentApplicantItem';
+import { CurrentApplicant } from '@/types/application';
+import { useModal } from '@/contexts/ModalContext';
+import { useParams } from 'next/navigation';
+
+const CurrentApplicants = ({
+ applicants,
+ filter,
+ onFilterChange,
+}: {
+ applicants: CurrentApplicant[];
+ filter: PositionType | undefined;
+ onFilterChange?: (value: PositionType | undefined) => void;
+}) => {
+ const { openModal } = useModal();
+ const [recruit, setRecruit] = useState(filter ?? undefined);
+
+ const params = useParams();
+ const recruitingPostId = params?.id ? Number(params.id) : undefined;
+
+ const handleApplicantClick = (applicationId: number, recruitingPostId: number) => {
+ if (!params) return;
+ openModal('partner', {
+ applicationId,
+ recruitingPostId,
+ profilePosition: applicants.find((app) => app.applicationId === applicationId)
+ ?.profilePosition,
+ });
+ };
+
+ // ํ๋ฉด ๋๋น์ ๋ฐ๋ผ cardStyle ๋ถ๊ธฐ
+ const [cardStyle, setCardStyle] = useState<'desktop' | 'mobile'>('mobile');
+
+ useEffect(() => {
+ setCardStyle(window.innerWidth >= 1024 ? 'desktop' : 'mobile');
+ const handleResize = () => {
+ setCardStyle(window.innerWidth >= 1024 ? 'desktop' : 'mobile');
+ };
+ window.addEventListener('resize', handleResize);
+ return () => window.removeEventListener('resize', handleResize);
+ }, []);
+
+ // ํํฐ ๊ด๋ฆฌ
+ const handleChange = (value: string) => {
+ if (value === 'ALL' || !value) {
+ setRecruit('ALL');
+ onFilterChange?.(undefined);
+ } else {
+ setRecruit(value);
+ onFilterChange?.(value as unknown as PositionType);
+ }
+ };
+
+ const filteredApplicants = filter
+ ? applicants.filter((item) => item.profilePosition === POSITION_KR[filter])
+ : applicants;
+
+ return (
+
+
+
+ {filteredApplicants.length > 0 && (
+
+ {filteredApplicants.map((item) => (
+ handleApplicantClick(item.applicationId, recruitingPostId!)}
+ />
+ ))}
+
+ )}
+
+ );
+};
+
+export default CurrentApplicants;
diff --git a/src/app/(main)/project/[id]/applicants/_components/InfoCard.tsx b/src/app/(main)/project/[id]/applicants/_components/InfoCard.tsx
new file mode 100644
index 00000000..cbdd5205
--- /dev/null
+++ b/src/app/(main)/project/[id]/applicants/_components/InfoCard.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import { CurrentRecruitingPost } from '@/types/application';
+import { formatDate } from '@/utils/project/formatDate';
+import InfoItem from '../../_components/InfoItem';
+
+const InfoCard = ({
+ startDate,
+ period,
+ progressWay,
+ contactWay,
+ recruitingDetails,
+}: CurrentRecruitingPost) => {
+ const formatPosition = recruitingDetails.join(', ');
+ const formatStartDate = formatDate(startDate);
+
+ return (
+
+ );
+};
+
+export default InfoCard;
diff --git a/src/app/(main)/project/[id]/applicants/_components/ProjectInfo.tsx b/src/app/(main)/project/[id]/applicants/_components/ProjectInfo.tsx
new file mode 100644
index 00000000..5fa9d739
--- /dev/null
+++ b/src/app/(main)/project/[id]/applicants/_components/ProjectInfo.tsx
@@ -0,0 +1,137 @@
+'use client';
+import DOMPurify from 'isomorphic-dompurify';
+import React, { useEffect, useState } from 'react';
+import ProjectTitle from './ProjectTitle';
+import { useGetProjectApplicants } from '@/hooks/queries/useProject';
+import Image from 'next/image';
+import InfoCard from './InfoCard';
+import CurrentApplicants from './CurrentApplicants';
+import { PositionType } from '@/utils/position';
+import Button from '@/components/common/Button';
+import { useModal } from '@/contexts/ModalContext';
+import { useParams } from 'next/dist/client/components/navigation';
+import { POSITION_KR } from '@/constants/Translate';
+import { useRouter } from 'next/navigation';
+import ErrorDisplay from '@/components/common/Error';
+import MobileHeader from '@/components/common/MobileHeader';
+
+const ProjectInfo = ({ id }: { id: string }) => {
+ const { openModal } = useModal();
+ const params = useParams();
+ const recruitingPostId = Number(params.id);
+
+ const handleFinishClick = (recruitingPostId: number) => {
+ if (!params) return;
+ openModal('applicateFinish', {
+ recruitingPostId,
+ });
+ };
+
+ const [isContentOpen, setIsContentOpen] = useState(false);
+ const [selectedPosition, setSelectedPosition] = useState(undefined);
+
+ const { data, isError } = useGetProjectApplicants({
+ postId: Number(id),
+ position: selectedPosition,
+ });
+
+ const router = useRouter();
+
+ useEffect(() => {
+ if (data?.recruitingPost.status === '๋ชจ์ง ๋ง๊ฐ') {
+ router.replace(`/project/${id}`);
+ }
+ }, [data?.recruitingPost.status, id, router]);
+
+ const handleFilterChange = (position?: PositionType) => {
+ setSelectedPosition(position);
+ };
+
+ if (isError)
+ return (
+
+
+
+ );
+ if (!data) return null;
+
+ const sanitizedContent = DOMPurify.sanitize(
+ data.recruitingPost.recruitingPostContent.replace(/<\/p>/g, '
'),
+ );
+
+ const filteredApplicants = selectedPosition
+ ? data.applicantList.filter((item) => item.profilePosition === POSITION_KR[selectedPosition])
+ : data.applicantList;
+ const hasAnyApplicants = filteredApplicants.length > 0;
+ const isFilterApplied = !!selectedPosition;
+
+ return (
+ <>
+
+
+
+
+
+ {isContentOpen && (
+
+ )}
+
+ {/* ์ง์์ ํํฉ */}
+
+ {!hasAnyApplicants && !isFilterApplied && (
+
+
์์ง ์ง์์๊ฐ ์์ด์
+
์กฐ๊ธ๋ง ๋ ๊ธฐ๋ค๋ ค๋ณผ๊น์?
+
+ )}
+
+ {!hasAnyApplicants && isFilterApplied && (
+
+
์ด ํํธ๋ ์์ง ์ง์์๊ฐ ์์ด์
+
์กฐ๊ธ๋ง ๋ ๊ธฐ๋ค๋ ค๋ณผ๊น์?
+
+ )}
+
+ {/* ์ง์์๊ฐ ์์ ๋๋ง ๋ฒํผ */}
+ {data.applicantList.length > 0 && (
+ <>
+
+
+
+
+ >
+ )}
+
+
+ >
+ );
+};
+
+export default ProjectInfo;
diff --git a/src/app/(main)/project/[id]/applicants/_components/ProjectTitle.tsx b/src/app/(main)/project/[id]/applicants/_components/ProjectTitle.tsx
new file mode 100644
index 00000000..826515a8
--- /dev/null
+++ b/src/app/(main)/project/[id]/applicants/_components/ProjectTitle.tsx
@@ -0,0 +1,29 @@
+'use client';
+
+import { CurrentRecruitingPost } from '@/types/application';
+import React from 'react';
+import DDay from '../../_components/DDayTag';
+
+const ProjectTitle = ({
+ recruitingPostTitle,
+ createdAt,
+ deadline,
+ dday,
+ status,
+}: CurrentRecruitingPost) => {
+ return (
+
+
+
+ {recruitingPostTitle}
+
+
+
+
+ {createdAt.split('T')[0]}~{deadline}
+
+
+ );
+};
+
+export default ProjectTitle;
diff --git a/src/app/(main)/project/[id]/applicants/page.tsx b/src/app/(main)/project/[id]/applicants/page.tsx
new file mode 100644
index 00000000..c2e16416
--- /dev/null
+++ b/src/app/(main)/project/[id]/applicants/page.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import ProjectInfo from './_components/ProjectInfo';
+
+type Props = {
+ params: Promise<{ id: string }>;
+};
+
+const page = async ({ params }: Props) => {
+ const { id } = await params;
+ return (
+ <>
+
+ >
+ );
+};
+
+export default page;
diff --git a/src/app/(main)/project/[id]/edit/_components/FormEditor.tsx b/src/app/(main)/project/[id]/edit/_components/FormEditor.tsx
new file mode 100644
index 00000000..e75cd23d
--- /dev/null
+++ b/src/app/(main)/project/[id]/edit/_components/FormEditor.tsx
@@ -0,0 +1,49 @@
+'use client';
+
+import React from 'react';
+import RecruitForm from '@/components/recruit/RecruitForm';
+import { useGetProject } from '@/hooks/queries/useProject';
+
+type Props = {
+ postId: number;
+};
+
+const FormEditor = ({ postId }: Props) => {
+ const {
+ data: projectData,
+ isLoading,
+ isError,
+ } = useGetProject({ postId }, { refetchOnMount: false, refetchOnWindowFocus: false });
+
+ if (isLoading) {
+ return (
+
+
ํ๋ก์ ํธ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋ ์ค...
+
+ );
+ }
+
+ if (isError || !projectData || !projectData.writer) {
+ return (
+
+
+ {!projectData?.writer
+ ? '์์ ๊ถํ์ด ์์ต๋๋ค.'
+ : 'ํ๋ก์ ํธ ์ ๋ณด๋ฅผ ๋ถ๋ฌ์ค๋๋ฐ ์คํจํ์ต๋๋ค.'}
+
+
+ );
+ }
+
+ return (
+ <>
+
+
ํ์ ๋ชจ์งํ๊ธฐ
+
๊ฒ์๊ธ์ ์์ฑํ์ฌ ์ํ๋ ๋ถ์ผ์ ํ์์ ๋ชจ์งํด๋ณด์ธ์
+
+
+ >
+ );
+};
+
+export default FormEditor;
diff --git a/src/app/(main)/project/[id]/edit/page.tsx b/src/app/(main)/project/[id]/edit/page.tsx
new file mode 100644
index 00000000..8fde989f
--- /dev/null
+++ b/src/app/(main)/project/[id]/edit/page.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import FormEditor from './_components/FormEditor';
+import MobileHeader from '@/components/common/MobileHeader';
+
+type Props = {
+ params: Promise<{
+ id: string;
+ }>;
+};
+
+const page = async ({ params }: Props) => {
+ const { id } = await params;
+ const postId = Number(id);
+
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default page;
diff --git a/src/app/(main)/project/[id]/page.tsx b/src/app/(main)/project/[id]/page.tsx
new file mode 100644
index 00000000..c2e16416
--- /dev/null
+++ b/src/app/(main)/project/[id]/page.tsx
@@ -0,0 +1,17 @@
+import React from 'react';
+import ProjectInfo from './_components/ProjectInfo';
+
+type Props = {
+ params: Promise<{ id: string }>;
+};
+
+const page = async ({ params }: Props) => {
+ const { id } = await params;
+ return (
+ <>
+
+ >
+ );
+};
+
+export default page;
diff --git a/src/app/(main)/project/_components/Banner.tsx b/src/app/(main)/project/_components/Banner.tsx
new file mode 100644
index 00000000..6d644368
--- /dev/null
+++ b/src/app/(main)/project/_components/Banner.tsx
@@ -0,0 +1,50 @@
+'use client';
+
+import LineButton from '@/components/common/button/LineButton';
+import { useToast } from '@/contexts/ToastContext';
+import { useUserStore } from '@/store/useUserStore';
+import { useRouter } from 'next/navigation';
+
+const Banner = () => {
+ const router = useRouter();
+ const { uuid } = useUserStore();
+ const { addToast } = useToast();
+
+ const handleClick = async () => {
+ if (!uuid) {
+ router.push('/login');
+ return;
+ }
+
+ router.push(`/teampsylog`);
+ const url = `${window.location.origin}/teampsylog/head/${uuid}`;
+ await navigator.clipboard.writeText(url);
+ addToast({ message: '๋งํฌ๊ฐ ๋ณต์ฌ๋์์ด์' });
+ };
+
+ return (
+ <>
+
+
+
+ ํํผ์
๋ก์์ ์ป์ ํค์๋๋ก
+
+ ํ๋ก์ ํธ๋ฅผ ํจ๊ป ํ ๋๋ฃ๋ฅผ ์ฐพ์๋ณด์ธ์
+
+
+ ํํผ์
๋ก ๊ณต์ ํ๊ธฐ โ
+
+ >
+ );
+};
+
+export default Banner;
diff --git a/src/app/(main)/project/_components/ButtonContainer.tsx b/src/app/(main)/project/_components/ButtonContainer.tsx
new file mode 100644
index 00000000..a2a7a40d
--- /dev/null
+++ b/src/app/(main)/project/_components/ButtonContainer.tsx
@@ -0,0 +1,99 @@
+'use client';
+
+import { useRouter } from 'next/navigation';
+import Button from '@/components/common/button/Button';
+import Toggle from '@/components/common/Toggle';
+import { DURATION_OPTIONS, RECRUIT_OPTIONS } from '@/constants/Dropdown';
+import { useState, useEffect } from 'react';
+import DropdownSmall from './Dropdown';
+
+type Filters = {
+ duration: string;
+ recruit: string;
+ onlyOpen: boolean;
+};
+
+type Props = {
+ filters?: Filters;
+ onChange?: (filters: Filters) => void;
+};
+
+const ButtonContainer = ({ filters, onChange }: Props) => {
+ const router = useRouter();
+ const [duration, setDuration] = useState(filters?.duration ?? '');
+ const [recruit, setRecruit] = useState(filters?.recruit ?? '');
+ const [onlyOpen, setOnlyOpen] = useState(filters?.onlyOpen ?? true);
+
+ const notifyChange = (updates: Partial) => {
+ const processedFilters = {
+ duration,
+ recruit: recruit === 'ALL' ? '' : recruit,
+ onlyOpen,
+ ...updates,
+ };
+
+ if (updates.recruit === 'ALL') {
+ processedFilters.recruit = '';
+ }
+ onChange?.(processedFilters);
+ };
+
+ const handleChange = (name: string, newValue: string) => {
+ if (name === 'duration') {
+ setDuration(newValue);
+ notifyChange({ duration: newValue });
+ } else if (name === 'recruit') {
+ setRecruit(newValue);
+ notifyChange({ recruit: newValue });
+ }
+ };
+
+ const handleToggleChange = (checked: boolean) => {
+ setOnlyOpen(checked);
+ notifyChange({ onlyOpen: checked });
+ };
+
+ useEffect(() => {
+ if (filters) {
+ if (filters.duration !== duration) setDuration(filters.duration);
+ if (filters.recruit !== recruit) setRecruit(filters.recruit);
+ if (filters.onlyOpen !== onlyOpen) setOnlyOpen(filters.onlyOpen);
+ }
+ }, [filters, duration, recruit, onlyOpen]);
+
+ const handleRecruitClick = () => {
+ router.push('/recruit');
+ };
+
+ return (
+
+
+
+ handleChange('duration', value)}
+ options={DURATION_OPTIONS}
+ />
+ handleChange('recruit', value)}
+ options={RECRUIT_OPTIONS}
+ />
+
+
+
+
+
+ );
+};
+
+export default ButtonContainer;
diff --git a/src/app/(main)/project/_components/Dropdown.tsx b/src/app/(main)/project/_components/Dropdown.tsx
new file mode 100644
index 00000000..8c4caa89
--- /dev/null
+++ b/src/app/(main)/project/_components/Dropdown.tsx
@@ -0,0 +1,165 @@
+'use client';
+
+import Image from 'next/image';
+import { useEffect, useMemo, useRef, useState } from 'react';
+
+type Option = { label: string; value: string };
+type DropdownProps = {
+ name?: string;
+ value?: string;
+ onChange?: (v: string) => void;
+ placeholder?: string;
+ options: Option[];
+ className?: string;
+ required?: boolean;
+};
+
+const DropdownSmall = ({
+ name,
+ value = '',
+ onChange,
+ placeholder = '',
+ options,
+ className = '',
+ required,
+}: DropdownProps) => {
+ const [open, setOpen] = useState(false);
+ const [openWidth, setOpenWidth] = useState(null);
+
+ const isPlaceholder = !value;
+ const wrapRef = useRef(null);
+ const buttonRef = useRef(null);
+ const menuRef = useRef(null);
+ const measureRef = useRef(null);
+
+ const selectedLabel = useMemo(
+ () => options.find((o) => o.value === value)?.label ?? '',
+ [options, value],
+ );
+
+ useEffect(() => {
+ const onDocClick = (e: MouseEvent) => {
+ if (!wrapRef.current) return;
+ if (!wrapRef.current.contains(e.target as Node)) setOpen(false);
+ };
+ document.addEventListener('mousedown', onDocClick);
+ return () => document.removeEventListener('mousedown', onDocClick);
+ }, []);
+
+ useEffect(() => {
+ if (!open) {
+ setOpenWidth(null);
+ return;
+ }
+
+ const measureWidth = () => {
+ const btnW = buttonRef.current?.getBoundingClientRect().width ?? 0;
+ const measW = measureRef.current?.getBoundingClientRect().width ?? 0;
+ const menuW = menuRef.current?.getBoundingClientRect().width ?? 0;
+ setOpenWidth(Math.max(btnW, measW, menuW));
+ };
+
+ measureWidth();
+ window.addEventListener('resize', measureWidth);
+ return () => window.removeEventListener('resize', measureWidth);
+ }, [open, options, selectedLabel, placeholder]);
+
+ return (
+
+
+
+ {open && (
+
+ {options.map((opt, idx) => {
+ const isSelected = opt.value === value;
+ return (
+ - {
+ onChange?.(opt.value);
+ setOpen(false);
+ }}
+ >
+ {opt.label}
+
+ );
+ })}
+
+ )}
+
+
+
+
+ {isPlaceholder ? placeholder : selectedLabel}
+
+
+
+
+ {options.map((o) => (
+
+ {o.label}
+
+ ))}
+
+
+
+ {name && (
+
+ )}
+
+ );
+};
+
+export default DropdownSmall;
diff --git a/src/app/(main)/project/_components/RecruitCard.tsx b/src/app/(main)/project/_components/RecruitCard.tsx
new file mode 100644
index 00000000..e77fab11
--- /dev/null
+++ b/src/app/(main)/project/_components/RecruitCard.tsx
@@ -0,0 +1,89 @@
+import { Tag } from '@/components/common/Tag';
+import { formatDateDot, formatDday } from '@/utils/project/formatDate';
+import Image from 'next/image';
+
+interface RecruitCardProps {
+ title: string;
+ hashtag: string;
+ author: string;
+ date: string;
+ profileImageUrl: string;
+ duration: string;
+ mode: string;
+ dday: number;
+ profileImage?: string;
+ status: string;
+ onClick?: () => void;
+}
+
+const RecruitCard = ({
+ title,
+ hashtag,
+ author,
+ date,
+ duration,
+ profileImageUrl,
+ profileImage = '/icons/profile.svg',
+ mode,
+ dday,
+ status,
+ onClick,
+}: RecruitCardProps) => {
+ const isClosed = status === 'CLOSED';
+ const finalProfileImage = profileImageUrl || profileImage;
+
+ return (
+ e.key === 'Enter' && onClick?.()}
+ >
+
+
+
+
+ {duration}
+
+
+ {mode}
+
+
+
+ {formatDday(dday)}
+
+
+
+
+ {title}
+
+
+ {hashtag}
+
+
+
+
+
+
+
+ {author}
+
+
+
+ {formatDateDot(date)}
+
+
+
+ );
+};
+
+export default RecruitCard;
diff --git a/src/app/(main)/project/page.tsx b/src/app/(main)/project/page.tsx
new file mode 100644
index 00000000..e6cbbf51
--- /dev/null
+++ b/src/app/(main)/project/page.tsx
@@ -0,0 +1,187 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import Banner from './_components/Banner';
+import ButtonContainer from './_components/ButtonContainer';
+import RecruitCard from './_components/RecruitCard';
+import Pagination from '../../../components/common/Pagination';
+import { useRouter } from 'next/navigation';
+import { ResponseProject } from '@/types/project';
+import { PERIOD_KR, POSITION_KR, PROGRESS_WAY_KR } from '@/constants/Translate';
+import {
+ Filters,
+ useRecruitingPosts,
+ useInfiniteRecruitingPosts,
+} from '@/hooks/queries/useRecruitingPosts';
+import Loading from '@/components/common/Loading';
+import ErrorDisplay from '@/components/common/Error';
+import Button from '@/components/common/button/Button';
+import { useInView } from 'react-intersection-observer';
+
+const Page = () => {
+ const router = useRouter();
+ const [currentPage, setCurrentPage] = useState(1);
+ const [filters, setFilters] = useState({
+ duration: '',
+ recruit: '',
+ onlyOpen: true,
+ });
+
+ const pageSize = 9;
+ const mobilePageSize = 10;
+ const initialDisplayCount = 5;
+ const loadMoreCount = 10;
+
+ const { data, isLoading, isError } = useRecruitingPosts(filters, currentPage, pageSize);
+
+ const {
+ data: infiniteData,
+ fetchNextPage,
+ hasNextPage,
+ isFetchingNextPage,
+ isLoading: isMobileLoading,
+ isError: isMobileError,
+ isInitialLoading: isMobileInitialLoading,
+ } = useInfiniteRecruitingPosts(filters, mobilePageSize);
+
+ const [displayCount, setDisplayCount] = useState(initialDisplayCount);
+
+ useEffect(() => {
+ setDisplayCount(initialDisplayCount);
+ }, [filters]);
+
+ const { ref, inView } = useInView({
+ threshold: 0,
+ });
+
+ const allMobileCards = infiniteData?.pages.flatMap((page) => page.content) ?? [];
+
+ const mobileCards = allMobileCards.slice(0, displayCount);
+
+ useEffect(() => {
+ if (inView && hasNextPage && !isFetchingNextPage) {
+ const nextDisplayCount = displayCount + loadMoreCount;
+
+ if (nextDisplayCount > allMobileCards.length) {
+ fetchNextPage();
+ } else {
+ setDisplayCount(nextDisplayCount);
+ }
+ }
+ }, [inView, hasNextPage, isFetchingNextPage, displayCount, allMobileCards.length, fetchNextPage]);
+
+ useEffect(() => {
+ if (infiniteData && allMobileCards.length > 0) {
+ const currentTotalCards = allMobileCards.length;
+ if (!hasNextPage && displayCount < currentTotalCards) {
+ setDisplayCount(currentTotalCards);
+ }
+ if (displayCount > currentTotalCards) {
+ setDisplayCount(Math.max(initialDisplayCount, currentTotalCards));
+ }
+ }
+ }, [infiniteData, hasNextPage, displayCount, allMobileCards.length]);
+
+ const handleFilterChange = (newFilters: Filters) => {
+ setFilters(newFilters);
+ setCurrentPage(1);
+ };
+
+ const isInitialLoading = (isLoading && currentPage === 1) || isMobileInitialLoading;
+
+ if (isInitialLoading) return ;
+ if (isError || isMobileError) return ;
+
+ const tabletCards = data?.content ?? [];
+ const totalPages = data?.totalPages ?? 1;
+
+ const handleCardClick = (id: number) => {
+ router.push(`/project/${id}`);
+ };
+
+ return (
+
+
+
+
+
+
+
+
+
+ {tabletCards.length > 0
+ ? tabletCards.map((card: ResponseProject) => (
+
`#${POSITION_KR[r.position] || r.position}`)
+ .join(' ')}
+ author={card.userName}
+ date={card.createdAt.split('T')[0]}
+ duration={PERIOD_KR[card.period] || card.period}
+ mode={PROGRESS_WAY_KR[card.progressWay] || card.progressWay}
+ dday={card.dday}
+ status={card.status}
+ onClick={() => handleCardClick(card.postId)}
+ />
+ ))
+ : !isLoading && (
+ ํ๋ก์ ํธ๊ฐ ์์ต๋๋ค.
+ )}
+
+
+ {mobileCards.length > 0 ? (
+ <>
+ {mobileCards.map((card: ResponseProject, index) => (
+
+ `#${POSITION_KR[r.position] || r.position}`)
+ .join(' ')}
+ author={card.userName}
+ date={card.createdAt.split('T')[0]}
+ duration={PERIOD_KR[card.period] || card.period}
+ mode={PROGRESS_WAY_KR[card.progressWay] || card.progressWay}
+ dday={card.dday}
+ status={card.status}
+ onClick={() => handleCardClick(card.postId)}
+ />
+
+ ))}
+ {isFetchingNextPage && mobileCards.length > initialDisplayCount && (
+
๋ถ๋ฌ์ค๋ ์ค...
+ )}
+ >
+ ) : (
+ !isMobileLoading && (
+
ํ๋ก์ ํธ๊ฐ ์์ต๋๋ค.
+ )
+ )}
+
+
+
+
+
+
+
+
+
setCurrentPage(page)}
+ />
+
+
+ );
+};
+
+export default Page;
diff --git a/src/app/(main)/question/[uuid]/_components/PartDropdown.tsx b/src/app/(main)/question/[uuid]/_components/PartDropdown.tsx
new file mode 100644
index 00000000..a8d4dbf1
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/_components/PartDropdown.tsx
@@ -0,0 +1,66 @@
+'use client';
+
+import { useRef, useState } from 'react';
+import clsx from 'clsx';
+import Image from 'next/image';
+import useOutsideClick from '@/hooks/useOutsideClick';
+
+const options = ['๊ธฐํ', '๋์์ธ', 'ํ๋ก ํธ์๋', '๋ฐฑ์๋'];
+
+interface PartDropdownProps {
+ selected: string | null;
+ onSelect: (value: string) => void;
+}
+
+const PartDropdown = ({ selected, onSelect }: PartDropdownProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+ useOutsideClick(dropdownRef, () => setIsOpen(false));
+
+ const handleSelect = (option: string) => {
+ onSelect(option);
+ setIsOpen(false);
+ };
+
+ const getIconSrc = () => {
+ if (!selected) return isOpen ? '/icons/question_gray_up.svg' : '/icons/question_gray_down.svg';
+ return isOpen ? '/icons/question_up.svg' : '/icons/question_down.svg';
+ };
+
+ return (
+
+
+
+ {isOpen && (
+
+ {options.map((option) => (
+ - handleSelect(option)}
+ className={clsx(
+ 'tablet:px-4.5 tablet:py-2 cursor-pointer border-b border-gray-300 px-3 py-2 text-gray-800 last:border-b-0',
+ selected === option && 'bg-primary-50',
+ )}
+ >
+ {option}
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default PartDropdown;
diff --git a/src/app/(main)/question/[uuid]/_components/QuestionAnswer.tsx b/src/app/(main)/question/[uuid]/_components/QuestionAnswer.tsx
new file mode 100644
index 00000000..2d1895aa
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/_components/QuestionAnswer.tsx
@@ -0,0 +1,59 @@
+'use client';
+
+import clsx from 'clsx';
+import { useEffect, useRef, useState } from 'react';
+
+interface QuestionAnswerProps {
+ value: string;
+ onChange: (value: string) => void;
+ hasError?: boolean;
+ maxLength?: number;
+}
+
+const QuestionAnswer = ({ value, onChange, hasError, maxLength = 250 }: QuestionAnswerProps) => {
+ const [charCount, setCharCount] = useState(0);
+ const textareaRef = useRef(null);
+
+ useEffect(() => {
+ setCharCount(value.length);
+ }, [value]);
+ const isOverLimit = charCount > maxLength;
+
+ useEffect(() => {
+ const textarea = textareaRef.current;
+ if (textarea) {
+ textarea.style.height = 'auto';
+ textarea.style.height = `${textarea.scrollHeight}px`;
+ }
+ }, [value]);
+
+ return (
+
+
+ );
+};
+
+export default QuestionAnswer;
diff --git a/src/app/(main)/question/[uuid]/_components/QuestionDropdown.tsx b/src/app/(main)/question/[uuid]/_components/QuestionDropdown.tsx
new file mode 100644
index 00000000..8d06e3c8
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/_components/QuestionDropdown.tsx
@@ -0,0 +1,82 @@
+'use client';
+
+import { useRef, useState } from 'react';
+import clsx from 'clsx';
+import Image from 'next/image';
+import useOutsideClick from '@/hooks/useOutsideClick';
+
+interface QuestionDropdownProps {
+ hasError?: boolean;
+ options: string[];
+ selected: string | null;
+ onSelect: (value: string) => void;
+ placeholder?: string;
+}
+
+const QuestionDropdown = ({
+ options,
+ selected,
+ onSelect,
+ hasError,
+ placeholder = '์ง๋ฌธ์ ์ ํํด์ฃผ์ธ์',
+}: QuestionDropdownProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+ useOutsideClick(dropdownRef, () => setIsOpen(false));
+
+ const handleSelect = (option: string) => {
+ onSelect(option);
+ setIsOpen(false);
+ };
+
+ return (
+
+
+
+ {isOpen && (
+
+ {options.map((option, index) => (
+ - handleSelect(option)}
+ className={clsx(
+ 'tablet:px-7 tablet:py-3 cursor-pointer px-5 py-3 text-gray-700',
+ selected === option && 'bg-primary-50',
+ index !== options.length - 1 && 'border-b border-gray-300',
+ )}
+ >
+ {option}
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default QuestionDropdown;
diff --git a/src/app/(main)/question/[uuid]/_components/QuestionFooter.tsx b/src/app/(main)/question/[uuid]/_components/QuestionFooter.tsx
new file mode 100644
index 00000000..89c1bd18
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/_components/QuestionFooter.tsx
@@ -0,0 +1,18 @@
+'use client';
+
+import Button from '@/components/common/button/Button';
+
+const QuestionFooter = () => {
+ return (
+
+
+
+ );
+};
+
+export default QuestionFooter;
diff --git a/src/app/(main)/question/[uuid]/_components/QuestionList.tsx b/src/app/(main)/question/[uuid]/_components/QuestionList.tsx
new file mode 100644
index 00000000..3aec3032
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/_components/QuestionList.tsx
@@ -0,0 +1,64 @@
+'use client';
+
+import { FieldErrors, UseFormSetValue, UseFormWatch } from 'react-hook-form';
+import { QuestionFormValues } from '@/libs/schemas/questionFormSchema';
+import QuestionDropdown from './QuestionDropdown';
+import { QUESTION_SETS } from '@/constants/Questions';
+import Image from 'next/image';
+import QuestionAnswer from './QuestionAnswer';
+
+interface QuestionListProps {
+ setValue: UseFormSetValue;
+ watch: UseFormWatch;
+ errors: FieldErrors;
+ userName: string;
+}
+
+const QuestionList = ({ setValue, watch, errors, userName }: QuestionListProps) => {
+ const QUESTION_SECTIONS = [
+ {
+ key: 'set1',
+ title: `${userName}๋๊ณผ ํจ๊ปํ๋ฉฐ ๋๋ ${userName}๋์ ๊ฐ์ `,
+ set: QUESTION_SETS.set1,
+ },
+ { key: 'set2', title: `${userName}๋๊ณผ ํจ๊ปํ๋ฉฐ ์ด๋ฐ ์ ์ด ์ข์์ด์`, set: QUESTION_SETS.set2 },
+ { key: 'set3', title: `${userName}๋๊ณผ ์ฑ์ฅ์ ์ํด์ ํด์ฃผ๊ณ ์ถ์ ๋ง`, set: QUESTION_SETS.set3 },
+ ] as const;
+
+ return (
+
+ {QUESTION_SECTIONS.map(({ key, title, set }) => (
+
+
+
+
setValue(`${key}.question`, value)}
+ hasError={!!errors[key]?.question}
+ />
+ {errors[key]?.question && (
+ {errors[key]?.question?.message}
+ )}
+ setValue(`${key}.answer`, text)}
+ hasError={!!errors[key]?.answer}
+ />
+ {errors[key]?.answer && (
+ {errors[key]?.answer?.message}
+ )}
+
+
+ ))}
+
+ );
+};
+
+export default QuestionList;
diff --git a/src/app/(main)/question/[uuid]/_components/QuestionTemplate.tsx b/src/app/(main)/question/[uuid]/_components/QuestionTemplate.tsx
new file mode 100644
index 00000000..28d7ecb5
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/_components/QuestionTemplate.tsx
@@ -0,0 +1,71 @@
+'use client';
+
+import { useModal } from '@/contexts/ModalContext';
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { questionFormSchema, QuestionFormValues } from '@/libs/schemas/questionFormSchema';
+import QuestionFooter from './QuestionFooter';
+import QuestionList from './QuestionList';
+import QuestionTitle from './QuestionTitle';
+import { useRequesterInfo } from '@/hooks/queries/useRequesterInfo';
+import Loading from '@/components/common/Loading';
+import ErrorDisplay from '@/components/common/Error';
+import { useRouter } from 'next/navigation';
+import { useUserStore } from '@/store/useUserStore';
+import { useEffect } from 'react';
+
+const QuestionTemplate = ({ uuid }: { uuid: string }) => {
+ const router = useRouter();
+ const { uuid: myUuid, _hasHydrated } = useUserStore();
+
+ useEffect(() => {
+ if (!_hasHydrated) return;
+
+ if (!myUuid) {
+ const currentPath = window.location.pathname;
+ localStorage.setItem('redirectAfterLogin', currentPath);
+ router.replace('/login');
+ }
+ }, [myUuid, _hasHydrated, router]);
+
+ const { openModal } = useModal();
+ const { data, isLoading, error } = useRequesterInfo(uuid);
+
+ const {
+ handleSubmit,
+ setValue,
+ watch,
+ formState: { errors },
+ } = useForm({
+ resolver: zodResolver(questionFormSchema),
+ defaultValues: {
+ set1: { question: '', answer: '' },
+ set2: { question: '', answer: '' },
+ set3: { question: '', answer: '' },
+ },
+ });
+
+ if (isLoading) return ;
+ if (error) return ;
+ if (!data) return null;
+
+ const { requesterName } = data;
+
+ const onSubmit = (data: QuestionFormValues) => {
+ openModal('teamPsylogAsk', {
+ userName: requesterName,
+ uuid: uuid,
+ formData: data,
+ });
+ };
+
+ return (
+
+ );
+};
+
+export default QuestionTemplate;
diff --git a/src/app/(main)/question/[uuid]/_components/QuestionTitle.tsx b/src/app/(main)/question/[uuid]/_components/QuestionTitle.tsx
new file mode 100644
index 00000000..b23a4321
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/_components/QuestionTitle.tsx
@@ -0,0 +1,22 @@
+'use client';
+
+import { useState } from 'react';
+import PartDropdown from './PartDropdown';
+
+const QuestionTitle = ({ userName }: { userName: string }) => {
+ const [selectedPart, setSelectedPart] = useState(null);
+
+ return (
+
+
+
+ ํํธ์์ ๋ณธ {userName}๋
+
+
+ ๊ฐ ์ง๋ฌธ๋ณ ์ํ๋ ์ง๋ฌธ์ ํ๋์ฉ ์ ํํ์ฌ ์๋ตํด๋ณด์ธ์
+
+
+ );
+};
+
+export default QuestionTitle;
diff --git a/src/app/(main)/question/[uuid]/page.tsx b/src/app/(main)/question/[uuid]/page.tsx
new file mode 100644
index 00000000..b82817fa
--- /dev/null
+++ b/src/app/(main)/question/[uuid]/page.tsx
@@ -0,0 +1,60 @@
+import React from 'react';
+import QuestionTemplate from './_components/QuestionTemplate';
+import { Metadata } from 'next';
+import axios from 'axios';
+import MobileHeader from '@/components/common/MobileHeader';
+
+type Props = {
+ params: Promise<{ uuid: string }>;
+};
+
+export async function generateMetadata({ params }: Props): Promise {
+ const { uuid } = await params;
+ let userName = '์ฌ์ฉ์';
+
+ try {
+ const res = await axios.get(
+ `${process.env.NEXT_PUBLIC_API_BASE_URL}/teamficial-log/requester`,
+ {
+ params: {
+ requesterUuid: uuid,
+ },
+ },
+ );
+
+ userName = res.data?.result?.requesterName ?? '์ฌ์ฉ์';
+ } catch (error) {
+ console.error('Failed to fetch requester data:', error);
+ }
+
+ return {
+ title: `${userName}๋์ ํํผ์
๋ก์ ์์ฑํด๋ณผ๊น์?`,
+ description: `์ํํธ์คํฌ ํ๋น๋ฉ ์๋น์ค, ํํผ์
`,
+ openGraph: {
+ title: `${userName}๋์ ํํผ์
๋ก์ ์์ฑํด๋ณผ๊น์?`,
+ description: `์ํํธ์คํฌ ํ๋น๋ฉ ์๋น์ค, ํํผ์
`,
+ images: [
+ {
+ url: '/og/Teamficial_metatag_Image.jpg',
+ width: 1200,
+ height: 630,
+ },
+ ],
+ },
+ };
+}
+
+const page = async ({ params }: Props) => {
+ const { uuid } = await params;
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default page;
diff --git a/src/app/(main)/recruit/page.tsx b/src/app/(main)/recruit/page.tsx
new file mode 100644
index 00000000..83670612
--- /dev/null
+++ b/src/app/(main)/recruit/page.tsx
@@ -0,0 +1,12 @@
+import React from 'react';
+import RecruitStepPage from '@/components/recruit/RecruitStepPage';
+
+const page = () => {
+ return (
+ <>
+
+ >
+ );
+};
+
+export default page;
diff --git a/src/app/(main)/teampsylog/[uuid]/_components/KeywordListPage.tsx b/src/app/(main)/teampsylog/[uuid]/_components/KeywordListPage.tsx
new file mode 100644
index 00000000..c48ba185
--- /dev/null
+++ b/src/app/(main)/teampsylog/[uuid]/_components/KeywordListPage.tsx
@@ -0,0 +1,18 @@
+'use client';
+
+import React from 'react';
+import { useParams } from 'next/navigation';
+import KeywordPage from '../../_components/KeywordPage';
+
+const KeywordListPage = () => {
+ const params = useParams();
+ const uuid = String(params.uuid);
+
+ return (
+ <>
+
+ >
+ );
+};
+
+export default KeywordListPage;
diff --git a/src/app/(main)/teampsylog/[uuid]/page.tsx b/src/app/(main)/teampsylog/[uuid]/page.tsx
new file mode 100644
index 00000000..bc9408c3
--- /dev/null
+++ b/src/app/(main)/teampsylog/[uuid]/page.tsx
@@ -0,0 +1,14 @@
+import React from 'react';
+import KeywordListPage from './_components/KeywordListPage';
+import MobileHeader from '@/components/common/MobileHeader';
+
+const page = () => {
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default page;
diff --git a/src/app/(main)/teampsylog/_components/AllKeyword.tsx b/src/app/(main)/teampsylog/_components/AllKeyword.tsx
new file mode 100644
index 00000000..2d0ecd35
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/AllKeyword.tsx
@@ -0,0 +1,117 @@
+import React from 'react';
+import Image from 'next/image';
+import { useIsMobile } from '@/hooks/useIsMobile';
+
+interface AllKeywordProps {
+ keywords: string[];
+ keywordIds: number[];
+ isEditMode: boolean;
+ selectedSlot: number | null;
+ onSelectKeyword?: (keywordId: number) => void;
+ onKeywordClick: (keywordId: number) => void;
+}
+
+type KeywordIconStyle = {
+ top: number;
+ rotate: string;
+ left?: number;
+ right?: number;
+};
+
+// ํค์๋ ์์ด์ฝ ์์น/ํ์ ์ ๋ณด ์์
+const KEYWORD_ICON_STYLES: KeywordIconStyle[] = [
+ { top: 49.8, left: 224, rotate: '14.781deg' },
+ { top: 166.8, left: 43, rotate: '0deg' },
+ { top: 277.5, left: 227, rotate: '6.665deg' },
+ { top: 381, left: 34, rotate: '14.074deg' },
+ { top: 540, left: 226, rotate: '-3.128deg' },
+];
+
+const MOBILE_KEYWORD_ICON_STYLES: KeywordIconStyle[] = [
+ { top: 166.8, right: 43, rotate: '-5.078deg' },
+ { top: 220, left: 23, rotate: '8.756deg' },
+ { top: 280, right: 30, rotate: '-10.873deg' },
+ { top: 361, left: 14, rotate: '0.803deg' },
+ { top: 420, right: 50, rotate: '4.223deg' },
+];
+
+const AllKeyword = ({
+ keywords,
+ keywordIds,
+ isEditMode,
+ selectedSlot,
+ onSelectKeyword,
+ onKeywordClick,
+}: AllKeywordProps) => {
+ const handleClick = (keywordId: number) => {
+ if (isEditMode && selectedSlot !== null) {
+ onSelectKeyword?.(keywordId);
+ } else if (!isEditMode) {
+ onKeywordClick(keywordId);
+ }
+ };
+
+ const isMobile = useIsMobile(1024);
+
+ return (
+
+ {/* ๋ฐฐ๊ฒฝ ๋ฐ์ดํ ์์ด์ฝ */}
+
+
+ {/* ํค์๋ ์์ด์ฝ 5๊ฐ */}
+ {keywords.slice(0, 5).map((keyword: string, idx: number) => {
+ const keywordId = keywordIds[idx];
+ const style = isMobile
+ ? MOBILE_KEYWORD_ICON_STYLES[idx] || {}
+ : KEYWORD_ICON_STYLES[idx] || {};
+ return (
+ handleClick(keywordId)}
+ />
+ );
+ })}
+
+ );
+};
+
+export default AllKeyword;
diff --git a/src/app/(main)/teampsylog/_components/BottomComment.tsx b/src/app/(main)/teampsylog/_components/BottomComment.tsx
new file mode 100644
index 00000000..ec00fbce
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/BottomComment.tsx
@@ -0,0 +1,68 @@
+'use client';
+
+import React from 'react';
+import useBottomSheetDrag from './useBottomSheetDrag';
+
+interface BottomSheetProps {
+ isOpen: boolean;
+ onClose: () => void;
+ children: React.ReactNode;
+}
+
+const BottomComment = ({ isOpen, onClose, children }: BottomSheetProps) => {
+ const {
+ dragCurrentY,
+ isDragging,
+ isClosing,
+ handleMouseDown,
+ handleTouchStart,
+ handleTouchMove,
+ handleTouchEnd,
+ handleClose,
+ shouldShow,
+ } = useBottomSheetDrag({ isOpen, onClose });
+
+ return (
+ <>
+
+ {/* ๋ฐํ
์ํธ */}
+
+ >
+ );
+};
+
+export default BottomComment;
diff --git a/src/app/(main)/teampsylog/_components/CommentPage.tsx b/src/app/(main)/teampsylog/_components/CommentPage.tsx
new file mode 100644
index 00000000..976a5a5c
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/CommentPage.tsx
@@ -0,0 +1,82 @@
+'use client';
+
+import React, { useEffect } from 'react';
+import { useInfiniteKeywordComment } from '@/hooks/queries/useKeyword';
+import { useInView } from 'react-intersection-observer';
+import { formatDateDot } from '@/utils/project/formatDate';
+import Image from 'next/image';
+import { useModal } from '@/contexts/ModalContext';
+
+const CommentPage = ({
+ keywordId,
+ keywordName,
+ isShareMode = false,
+}: {
+ keywordId: number;
+ keywordName: string;
+ isShareMode?: boolean;
+}) => {
+ const { openModal } = useModal();
+ const { data, fetchNextPage, hasNextPage, isFetchingNextPage } = useInfiniteKeywordComment({
+ keywordId,
+ size: 4,
+ });
+
+ const onReportClick = (keywordCommentId: number) => {
+ openModal('reportComment', { keywordCommentId });
+ };
+
+ const comments = data?.pages.flatMap((page) => page.data) || [];
+
+ const { ref, inView } = useInView();
+ useEffect(() => {
+ if (inView && hasNextPage && !isFetchingNextPage) {
+ fetchNextPage();
+ }
+ }, [inView, hasNextPage, isFetchingNextPage, fetchNextPage]);
+
+ return (
+
+
+
{`โ${keywordName}โ์ ๋ํ ๋ค๋ฅธ ์ฌ๋๋ค์ ์ฝ๋ฉํธ์์`}
+
์ค๋ณต๋ ํค์๋๋ ๋งํ์ ์ด ์ปค์ ธ์
+
+
+ {comments.map((comment, idx) => (
+
+
+
+
+ {formatDateDot(comment.createdAt)}
+
+ {isShareMode ? null : (
+
+ )}
+
+
+
+
{comment.comment}
+
+ ))}
+
+ {hasNextPage && (
+
+ {isFetchingNextPage ? '๋ถ๋ฌ์ค๋ ์ค...' : '์๋๋ก ์คํฌ๋กคํ์ฌ ๋ ๋ณด๊ธฐ'}
+
+ )}
+
+ );
+};
+
+export default CommentPage;
diff --git a/src/app/(main)/teampsylog/_components/KeywordBar.tsx b/src/app/(main)/teampsylog/_components/KeywordBar.tsx
new file mode 100644
index 00000000..b89bf95f
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/KeywordBar.tsx
@@ -0,0 +1,170 @@
+import { useGetKeyword } from '@/hooks/queries/useKeyword';
+import React, { useEffect, useState } from 'react';
+import KeywordItem from './KeywordItem';
+import Image from 'next/image';
+import ProfileDropdown from './ProfileDropdown';
+import { ResponseProfile } from '@/types/profile';
+import { useToast } from '@/contexts/ToastContext';
+import KeywordGuideBalloon from './KeywordGuideBalloon';
+
+const KeywordBar = ({
+ profileId,
+ profiles,
+ selectedProfileId,
+ onSelectProfile,
+ isEditMode,
+ onToggleEditMode,
+ selectedSlot,
+ onSelectSlot,
+ isShareMode = false,
+}: {
+ profileId: number;
+ profiles: ResponseProfile[];
+ selectedProfileId: number | null;
+ onSelectProfile: (profileId: number) => void;
+ isEditMode: boolean;
+ onToggleEditMode: () => void;
+ selectedSlot: number | null;
+ onSelectSlot: (index: number) => void;
+ isShareMode?: boolean;
+}) => {
+ const { data } = useGetKeyword({ profileId });
+ const { addToast } = useToast();
+ const [showGuide, setShowGuide] = useState(false);
+
+ const headKeywords = data?.headKeywords || [];
+ const desktopDisplayKeywords = [
+ ...headKeywords.map((kw) => kw.headKeywordName),
+ ...Array(Math.max(0, 3 - headKeywords.length)).fill('ํค์๋๋ฅผ ์ ํํ์ธ์'),
+ ];
+ const mobileDisplayKeywords = [
+ ...headKeywords.map((kw) => kw.headKeywordName),
+ ...Array(Math.max(0, 3 - headKeywords.length)).fill('ํค์๋ ์ ํ'),
+ ];
+
+ const handleShare = async () => {
+ let uuid: string | undefined;
+ if (typeof window !== 'undefined') {
+ const userString = localStorage.getItem('user');
+ if (userString) {
+ try {
+ const userObj = JSON.parse(userString);
+ uuid = userObj?.state?.uuid;
+ } catch (e) {
+ console.log('Error parsing user from localStorage:', e);
+ uuid = undefined;
+ }
+ }
+ }
+ const url = uuid
+ ? `${window.location.origin}/teampsylog/head/${uuid}`
+ : `${window.location.origin}/teampsylog`;
+ await navigator.clipboard.writeText(url);
+ addToast({ message: '๋งํฌ๊ฐ ๋ณต์ฌ๋์์ด์' });
+ };
+
+ useEffect(() => {
+ // ํธ์ง ๋ชจ๋์ด๊ณ ์์ง ์ฌ๋กฏ์ด ์ ํ๋์ง ์์์ ๋๋ง ๊ฐ์ด๋ ํ์
+ if (isEditMode && selectedSlot === null) {
+ setShowGuide(true);
+ } else {
+ setShowGuide(false);
+ }
+ }, [isEditMode, selectedSlot]);
+
+ if (!isShareMode) return null;
+
+ return (
+ <>
+ {/* desktop */}
+
+
+ {isEditMode && showGuide && (
+
setShowGuide(false)} />
+ )}
+
+ {/* ๋ํํค์๋ ๋ฐ ํ๋กํ ๋๋กญ๋ค์ด */}
+
+
+ {desktopDisplayKeywords.map((keyword, index) => (
+
= headKeywords.length}
+ onClick={() => isEditMode && onSelectSlot(index)}
+ />
+ ))}
+
+ {isShareMode && (
+
+
+
+
+ )}
+
+
+
+
+ {/* mobile */}
+
+ {/* ์์ ๋ฒํผ ๋ฐ ํ๋กํ ๋๋กญ๋ค์ด */}
+
+
+
+ {isEditMode && showGuide && (
+
setShowGuide(false)} />
+ )}
+
+ {mobileDisplayKeywords.map((keyword, index) => (
+ = headKeywords.length}
+ onClick={() => isEditMode && onSelectSlot(index)}
+ isMobileDevice={true}
+ />
+ ))}
+
+
+
+ >
+ );
+};
+
+export default KeywordBar;
diff --git a/src/app/(main)/teampsylog/_components/KeywordGuideBalloon.tsx b/src/app/(main)/teampsylog/_components/KeywordGuideBalloon.tsx
new file mode 100644
index 00000000..5d97cf5f
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/KeywordGuideBalloon.tsx
@@ -0,0 +1,41 @@
+import React from 'react';
+
+interface KeywordGuideBalloonProps {
+ position: 'top' | 'bottom';
+ onClose: () => void;
+ text?: string;
+}
+
+const KeywordGuideBalloon: React.FC = ({
+ position,
+ onClose,
+ text = '๋ณ๊ฒฝํ ๋ํ ํค์๋๋ฅผ\n๋จผ์ ์ ํํ์ธ์',
+}) => {
+ return (
+
+
+ {text}
+
+
+ {/* ๊ผฌ๋ฆฌ */}
+
+
+ );
+};
+
+export default KeywordGuideBalloon;
diff --git a/src/app/(main)/teampsylog/_components/KeywordGuideOverlay.tsx b/src/app/(main)/teampsylog/_components/KeywordGuideOverlay.tsx
new file mode 100644
index 00000000..22a5970c
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/KeywordGuideOverlay.tsx
@@ -0,0 +1,153 @@
+'use client';
+
+import Button from '@/components/common/button/Button';
+import Image from 'next/image';
+import React, { useEffect, useState, useRef } from 'react';
+import { Swiper, SwiperSlide } from 'swiper/react';
+import type { Swiper as SwiperType } from 'swiper';
+import 'swiper/css';
+import 'swiper/css/pagination';
+
+interface Props {
+ onClose: () => void;
+}
+
+const KeywordGuideOverlay = ({ onClose }: Props) => {
+ const [checked, setChecked] = useState(false);
+
+ const handleClick = () => {
+ const newValue = !checked;
+ setChecked(newValue);
+
+ if (newValue) {
+ try {
+ if (typeof window !== 'undefined') {
+ localStorage.setItem('hideKeywordGuide', 'true');
+ }
+ } catch (error) {
+ console.error('Failed to save guide preference:', error);
+ }
+ onClose();
+ }
+ };
+
+ useEffect(() => {
+ const handleEscape = (e: KeyboardEvent) => {
+ if (e.key === 'Escape') {
+ onClose();
+ }
+ };
+
+ window.addEventListener('keydown', handleEscape);
+ return () => window.removeEventListener('keydown', handleEscape);
+ }, [onClose]);
+
+ // Swiper instance and active index for mobile dot indicator
+ const swiperRef = useRef(null);
+ const [activeIndex, setActiveIndex] = useState(0);
+
+ return (
+
+ {/* desktop */}
+
+
+
+
+
+
+
+
+
+ {/* mobile */}
+
+
+ {
+ swiperRef.current = swiper;
+ setActiveIndex(swiper.activeIndex);
+ }}
+ onSlideChange={(swiper) => setActiveIndex(swiper.activeIndex)}
+ >
+
+
+
+
+
+
+
+
+
+
+ {[0, 1].map((idx) => (
+
+ ))}
+
+
+
+
+
+
+ );
+};
+
+export default KeywordGuideOverlay;
diff --git a/src/app/(main)/teampsylog/_components/KeywordItem.tsx b/src/app/(main)/teampsylog/_components/KeywordItem.tsx
new file mode 100644
index 00000000..48f0476a
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/KeywordItem.tsx
@@ -0,0 +1,87 @@
+import React from 'react';
+import { useTruncatedTooltip } from '@/hooks/useTruncatedTooltip';
+
+interface KeywordItemProps {
+ keyword: string;
+ isEditMode?: boolean;
+ isSelected?: boolean;
+ isPlaceholder?: boolean;
+ onClick?: () => void;
+ isMobileDevice?: boolean;
+}
+
+const KeywordItem = ({
+ keyword,
+ isEditMode = false,
+ isSelected = false,
+ isPlaceholder = false,
+ onClick,
+ isMobileDevice = false,
+}: KeywordItemProps) => {
+ // limit: ๋ชจ๋ฐ์ผ 5, ๋ฐ์คํฌํ 9999 (์ฌ์ค์ ๋ฌด์ ํ)
+ const maxLength = isMobileDevice ? 5 : 9999;
+
+ const {
+ ref: spanRef,
+ showTooltip,
+ displayText,
+ isTruncated,
+ handleMouseEnter,
+ handleMouseLeave,
+ handleTouch,
+ } = useTruncatedTooltip({ text: keyword, maxLength, isMobile: isMobileDevice });
+
+ const handleFocus = () => {
+ if (!isMobileDevice && isTruncated) {
+ handleMouseEnter(undefined as unknown as React.MouseEvent);
+ }
+ };
+ const handleBlur = () => {
+ handleMouseLeave();
+ };
+
+ return (
+
+ }
+ onTouchStart={handleTouch as React.TouchEventHandler}
+ onMouseEnter={handleMouseEnter as React.MouseEventHandler}
+ onMouseLeave={handleMouseLeave as React.MouseEventHandler}
+ onFocus={handleFocus}
+ onBlur={handleBlur}
+ tabIndex={!isEditMode && isTruncated ? 0 : undefined}
+ className="relative cursor-pointer"
+ aria-label={isTruncated ? keyword : undefined}
+ >
+ #{displayText}
+ {showTooltip && (
+
+ #{keyword}
+
+ )}
+
+
+ );
+};
+
+export default KeywordItem;
diff --git a/src/app/(main)/teampsylog/_components/KeywordPage.tsx b/src/app/(main)/teampsylog/_components/KeywordPage.tsx
new file mode 100644
index 00000000..08050962
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/KeywordPage.tsx
@@ -0,0 +1,233 @@
+'use client';
+
+import React, { useState, useEffect, useMemo, useRef } from 'react';
+import { useRouter } from 'next/navigation';
+import { useUserStore } from '@/store/useUserStore';
+import { isLoggedIn as checkIsLoggedIn } from '@/utils/auth';
+import LogTitle from './LogTitle';
+import { useGetProfileList, useGetUuidProfileList } from '@/hooks/queries/useProfile';
+import { useGetKeyword } from '@/hooks/queries/useKeyword';
+import KeywordBar from './KeywordBar';
+import LogNote from './LogNote';
+import { useRequesterInfo } from '@/hooks/queries/useRequesterInfo';
+import { useUpdateHeadKeywords } from '@/hooks/mutation/useUpdateHeadKeywords';
+import { useToast } from '@/contexts/ToastContext';
+import Loading from '@/components/common/Loading';
+import KeywordGuideOverlay from './KeywordGuideOverlay';
+
+interface Props {
+ share?: boolean;
+ uuid?: string;
+}
+
+const KeywordPage = ({ share = false, uuid }: Props) => {
+ const router = useRouter();
+ const { addToast } = useToast();
+ const { userName, _hasHydrated } = useUserStore();
+
+ const hasAlerted = useRef(false);
+
+ useEffect(() => {
+ if (!_hasHydrated) return;
+ if (hasAlerted.current) return;
+
+ // ๋ก๊ทธ์ธ ์ฒดํฌ
+ const loggedIn = checkIsLoggedIn(userName);
+
+ if (!loggedIn) {
+ hasAlerted.current = true;
+ addToast({
+ type: 'error',
+ title: '๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.',
+ message: '๋ก๊ทธ์ธ ํ์ด์ง๋ก ์ด๋ํฉ๋๋ค.',
+ });
+
+ // ํ์ฌ ๊ฒฝ๋ก ์ ์ฅ
+ if (typeof window !== 'undefined') {
+ const currentPath = window.location.pathname;
+ localStorage.setItem('redirectAfterLogin', currentPath);
+ }
+
+ router.replace('/login');
+ }
+ }, [_hasHydrated, userName, router, addToast]);
+
+ // ๊ณต์ ๋ชจ๋: uuid๊ฐ ์์ ๋๋ง ์คํ
+ const requesterInfoResult = useRequesterInfo(uuid ?? '', { enabled: share && !!uuid });
+ const requesterInfo = requesterInfoResult.data;
+ const shareUserId = share && requesterInfo ? requesterInfo.userId : null;
+ const requesterName = requesterInfo?.requesterName;
+
+ // ๊ณต์ ๋ชจ๋ getProfileList ๊ฐ์ ธ์ค๊ธฐ
+ const { data: sharedProfiles } = useGetUuidProfileList(uuid ?? '');
+
+ // ์ผ๋ฐ ๋ชจ๋
+ const { data: myProfiles } = useGetProfileList();
+ // share ๋ชจ๋์ ๋ฐ๋ผ ํ๋กํ ๊ฒฐ์
+ const profiles = useMemo(() => {
+ if (share) {
+ return sharedProfiles || [];
+ }
+ return myProfiles || [];
+ }, [share, sharedProfiles, myProfiles]);
+
+ const [selectedProfileId, setSelectedProfileId] = useState(null);
+ const [localUserId, setLocalUserId] = useState(null);
+
+ // ํธ์ง ๋ชจ๋ ์ํ
+ const [isEditMode, setIsEditMode] = useState(false);
+ const [selectedSlot, setSelectedSlot] = useState(null);
+
+ const [showGuide, setShowGuide] = useState(false);
+
+ useEffect(() => {
+ if (share) return;
+
+ const hide = localStorage.getItem('hideKeywordGuide');
+ if (!hide) {
+ setShowGuide(true);
+ }
+ }, [share]);
+
+ const { mutate: updateHeadKeywords } = useUpdateHeadKeywords();
+
+ const { data: keywordData, isLoading } = useGetKeyword({
+ profileId: selectedProfileId ?? 0,
+ });
+
+ useEffect(() => {
+ if (share) return;
+
+ try {
+ const userString = localStorage.getItem('user');
+ if (!userString) return;
+ const userData = JSON.parse(userString);
+ const userId = userData?.state?.userId;
+ if (userId) setLocalUserId(userId);
+ } catch {
+ setLocalUserId(null);
+ }
+ }, [share]);
+
+ useEffect(() => {
+ if (profiles.length > 0 && selectedProfileId === null) {
+ setSelectedProfileId(profiles[0].profileId);
+ }
+ }, [profiles, selectedProfileId]);
+
+ const handleToggleEditMode = () => {
+ setIsEditMode(!isEditMode);
+ if (isEditMode) {
+ setSelectedSlot(null);
+ }
+ };
+
+ const handleSelectSlot = (index: number) => {
+ if (!isEditMode) return;
+ setSelectedSlot(index);
+ };
+
+ const handleSelectKeyword = (keywordId: number) => {
+ if (!isEditMode || selectedSlot === null || !selectedProfileId) return;
+
+ const headKeywords = keywordData?.headKeywords || [];
+ const oldHeadKeywordId =
+ selectedSlot < headKeywords.length ? headKeywords[selectedSlot].headKeywordId : undefined;
+
+ updateHeadKeywords(
+ {
+ profileId: selectedProfileId,
+ keywordId,
+ oldHeadKeywordId: oldHeadKeywordId ?? 0,
+ },
+ {
+ onSuccess: () => {
+ setSelectedSlot(null);
+ setIsEditMode(false);
+ },
+ onError: () => {
+ addToast({
+ type: 'error',
+ title: '์ด๋ฏธ ๋ฑ๋ก๋ ๋ํํค์๋์
๋๋ค.',
+ message: '๋ฑ๋กํ์ง ์์ ํค์๋๋ฅผ ์ ํํด์ฃผ์ธ์.',
+ });
+ },
+ },
+ );
+ };
+
+ // hydration ์๋ฃ ๋๊ธฐ ์ค์ด๋ฉด Loading ํ์
+ if (!_hasHydrated) {
+ return ;
+ }
+
+ // ๋ก๊ทธ์ธ๋์ง ์์์ผ๋ฉด Loading ํ์ (๋ฆฌ๋ค์ด๋ ํธ ๋๊ธฐ)
+ if (!checkIsLoggedIn(userName)) {
+ return ;
+ }
+
+ // ๋ฐ์ดํฐ ๋ก๋ฉ ์ค
+ if (isLoading || !keywordData) {
+ return ;
+ }
+
+ return (
+
+ {!share && showGuide && setShowGuide(false)} />}
+
+ {/* ์ ๋ชฉ */}
+
+
+ {/* ํค์๋ ๋ฐ */}
+ {selectedProfileId !== null && (
+
+ )}
+ {/* ํํผ์
๋ก ๋
ธํธ */}
+
+
+ );
+};
+
+export default KeywordPage;
diff --git a/src/app/(main)/teampsylog/_components/LogNote.tsx b/src/app/(main)/teampsylog/_components/LogNote.tsx
new file mode 100644
index 00000000..40cff70c
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/LogNote.tsx
@@ -0,0 +1,200 @@
+'use client';
+
+import { useGetKeywordList } from '@/hooks/queries/useKeyword';
+import Image from 'next/image';
+import React, { useEffect, useState } from 'react';
+import CommentPage from './CommentPage';
+import AllKeyword from './AllKeyword';
+import Loading from '@/components/common/Loading';
+import ErrorDisplay from '@/components/common/Error';
+import BottomComment from './BottomComment';
+import KeywordGuideBalloon from './KeywordGuideBalloon';
+
+const LogNote = ({
+ userId,
+ isEditMode = false,
+ selectedSlot = null,
+ onSelectKeyword,
+ isShareMode = false,
+}: {
+ userId?: number;
+ isEditMode?: boolean;
+ selectedSlot?: number | null;
+ onSelectKeyword?: (keywordId: number) => void;
+ isShareMode?: boolean;
+}) => {
+ const [page, setPage] = useState(0);
+ const [isMobileSheetOpen, setIsMobileSheetOpen] = useState(false);
+ const [showGuide, setShowGuide] = useState(false);
+
+ const { data, isLoading, isError } = useGetKeywordList({
+ userId: userId ?? 0,
+ page,
+ size: 5,
+ });
+
+ // ํค์๋ ๋ชฉ๋ก
+ const keywords = data?.content?.map((item) => item.keywordName) || [];
+ const keywordIds = data?.content?.map((item) => item.keywordId) || [];
+ const totalPages = Math.ceil((data?.totalElements || 1) / 5);
+
+ const [selectedKeywordId, setSelectedKeywordId] = useState(null);
+
+ useEffect(() => {
+ setSelectedKeywordId(null);
+ }, [page]);
+
+ useEffect(() => {
+ if (isEditMode && selectedSlot !== null) {
+ setShowGuide(true);
+ } else {
+ setShowGuide(false);
+ }
+ }, [isEditMode, selectedSlot]);
+
+ const handleKeywordClick = (keywordId: number) => {
+ if (isEditMode && selectedSlot !== null) {
+ // ํธ์ง ๋ชจ๋: ์ ํ๋ ์ฌ๋กฏ์ ํค์๋ ํ ๋น
+ onSelectKeyword?.(keywordId);
+ } else if (!isEditMode) {
+ // ์ผ๋ฐ ๋ชจ๋: ๋๊ธ ๋ณด๊ธฐ
+ setSelectedKeywordId(keywordId);
+ setIsMobileSheetOpen(true);
+ }
+ };
+
+ const closeMobileSheet = () => {
+ setIsMobileSheetOpen(false);
+ };
+
+ return (
+ <>
+ {isLoading ? (
+
+ ) : isError ? (
+
+ ) : (
+
+ {/* ์ผ์ชฝ ํ์ด์ง */}
+
+ {showGuide && (
+
+ setShowGuide(false)}
+ text={`๋ณ๊ฒฝํ ํค์๋๋ฅผ\n์ ํํ์ธ์`}
+ />
+
+ )}
+
+
+
+
+ {data?.totalElements === 0 ? (
+ <>
+
+
+ {`๋์ ํํผ์
๋ก ๋งํฌ๋ฅผ ๊ณต์ ํ๊ณ \nํค์๋๋ฅผ ๋ฐ์๋ณด์ธ์`}
+
+ >
+ ) : (
+
+ )}
+
+
+ {/* ์ค๋ฅธ์ชฝ ํ์ด์ง (๋ฐ์คํฌํฑ๋ง) */}
+
+
+ {!selectedKeywordId ? (
+
+ ) : (
+ id === selectedKeywordId)] ?? ''}
+ keywordId={selectedKeywordId}
+ isShareMode={isShareMode}
+ />
+ )}
+
+ {/* ๋ชจ๋ฐ์ผ ๋ฐํ
์ํธ */}
+
+ {selectedKeywordId && (
+ id === selectedKeywordId)] ?? ''}
+ keywordId={selectedKeywordId}
+ isShareMode={isShareMode}
+ />
+ )}
+
+
+ )}
+ >
+ );
+};
+
+export default LogNote;
diff --git a/src/app/(main)/teampsylog/_components/LogTitle.tsx b/src/app/(main)/teampsylog/_components/LogTitle.tsx
new file mode 100644
index 00000000..b683a9ce
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/LogTitle.tsx
@@ -0,0 +1,20 @@
+'use client';
+
+import { ResponseProfile } from '@/types/profile';
+import React from 'react';
+
+type LogTitleProps = {
+ profiles: ResponseProfile[];
+};
+
+const LogTitle = ({ profiles }: LogTitleProps) => {
+ const userName = profiles[0]?.userName || '์ฌ์ฉ์';
+
+ return (
+
+
{userName}๋์ ํํผ์
๋ก
+
+ );
+};
+
+export default LogTitle;
diff --git a/src/app/(main)/teampsylog/_components/ProfileDropdown.tsx b/src/app/(main)/teampsylog/_components/ProfileDropdown.tsx
new file mode 100644
index 00000000..531c4193
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/ProfileDropdown.tsx
@@ -0,0 +1,65 @@
+import { useIsMobile } from '@/hooks/useIsMobile';
+import useOutsideClick from '@/hooks/useOutsideClick';
+import { ResponseProfile } from '@/types/profile';
+import Image from 'next/image';
+import { useRef, useState } from 'react';
+
+// ๋๋กญ๋ค์ด๋ง ๋ณ๋ ์ปดํฌ๋ํธ๋ก ๋ถ๋ฆฌ
+const ProfileDropdown = ({
+ profiles,
+ selectedProfileId,
+ onSelectProfile,
+}: {
+ profiles: ResponseProfile[];
+ selectedProfileId: number | null;
+ onSelectProfile: (profileId: number) => void;
+}) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const dropdownRef = useRef(null);
+ const selectedProfile = profiles.find((p) => p.profileId === selectedProfileId);
+
+ useOutsideClick(dropdownRef, () => setIsOpen(false));
+
+ const isMobile = useIsMobile(1024);
+
+ return (
+
+
setIsOpen(!isOpen)}
+ >
+
{selectedProfile?.profileName || '์ ํ๋กํ'}
+
+
+ {isOpen && profiles.length > 0 && (
+
+ {profiles.map((profile) => (
+
{
+ onSelectProfile(profile.profileId);
+ setIsOpen(false);
+ }}
+ >
+ {profile.profileName || '์ ํ๋กํ'}
+
+ ))}
+
+ )}
+
+ );
+};
+
+export default ProfileDropdown;
diff --git a/src/app/(main)/teampsylog/_components/useBottomSheetDrag.ts b/src/app/(main)/teampsylog/_components/useBottomSheetDrag.ts
new file mode 100644
index 00000000..a63978a6
--- /dev/null
+++ b/src/app/(main)/teampsylog/_components/useBottomSheetDrag.ts
@@ -0,0 +1,121 @@
+import { useCallback, useEffect, useRef, useState } from 'react';
+
+interface UseBottomSheetDragProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+const useBottomSheetDrag = ({ isOpen, onClose }: UseBottomSheetDragProps) => {
+ const closeTimerRef = useRef(null);
+
+ const [dragStartY, setDragStartY] = useState(0);
+ const [dragCurrentY, setDragCurrentY] = useState(0);
+ const [isDragging, setIsDragging] = useState(false);
+ const [isClosing, setIsClosing] = useState(false);
+
+ const handleClose = useCallback(() => {
+ if (closeTimerRef.current) {
+ clearTimeout(closeTimerRef.current);
+ }
+ setIsClosing(true);
+ closeTimerRef.current = setTimeout(() => {
+ onClose();
+ setIsClosing(false);
+ }, 300);
+ }, [onClose]);
+
+ useEffect(() => {
+ return () => {
+ if (closeTimerRef.current) {
+ clearTimeout(closeTimerRef.current);
+ }
+ };
+ }, []);
+
+ const handleMouseMove = useCallback(
+ (e: MouseEvent) => {
+ if (!isDragging) return;
+ const currentY = e.clientY;
+ const diff = currentY - dragStartY;
+ if (diff > 0) {
+ setDragCurrentY(diff);
+ }
+ },
+ [isDragging, dragStartY],
+ );
+
+ const handleMouseUp = useCallback(() => {
+ if (!isDragging) return;
+ if (dragCurrentY > 150) {
+ handleClose();
+ setDragCurrentY(0);
+ } else {
+ setDragCurrentY(0);
+ }
+ setIsDragging(false);
+ }, [isDragging, dragCurrentY, handleClose]);
+
+ const handleTouchStart = (e: React.TouchEvent) => {
+ setDragStartY(e.touches[0].clientY);
+ setIsDragging(true);
+ };
+
+ const handleTouchMove = (e: React.TouchEvent) => {
+ if (!isDragging) return;
+ const currentY = e.touches[0].clientY;
+ const diff = currentY - dragStartY;
+ if (diff > 0) {
+ setDragCurrentY(diff);
+ }
+ };
+
+ const handleTouchEnd = () => {
+ if (!isDragging) return;
+ if (dragCurrentY > 150) {
+ handleClose();
+ setDragCurrentY(0);
+ } else {
+ setDragCurrentY(0);
+ }
+ setIsDragging(false);
+ };
+
+ const handleMouseDown = (e: React.MouseEvent) => {
+ setDragStartY(e.clientY);
+ setIsDragging(true);
+ };
+
+ useEffect(() => {
+ if (isDragging) {
+ window.addEventListener('mousemove', handleMouseMove);
+ window.addEventListener('mouseup', handleMouseUp);
+ }
+ return () => {
+ window.removeEventListener('mousemove', handleMouseMove);
+ window.removeEventListener('mouseup', handleMouseUp);
+ };
+ }, [isDragging, handleMouseMove, handleMouseUp]);
+
+ useEffect(() => {
+ if (!isOpen) {
+ setDragCurrentY(0);
+ setIsDragging(false);
+ }
+ }, [isOpen]);
+
+ const shouldShow = isOpen || isClosing;
+
+ return {
+ dragCurrentY,
+ isDragging,
+ isClosing,
+ handleMouseDown,
+ handleTouchStart,
+ handleTouchMove,
+ handleTouchEnd,
+ handleClose,
+ shouldShow,
+ };
+};
+
+export default useBottomSheetDrag;
diff --git a/src/app/(main)/teampsylog/head/[uuid]/_components/KeywordCard.tsx b/src/app/(main)/teampsylog/head/[uuid]/_components/KeywordCard.tsx
new file mode 100644
index 00000000..1e0a27d2
--- /dev/null
+++ b/src/app/(main)/teampsylog/head/[uuid]/_components/KeywordCard.tsx
@@ -0,0 +1,106 @@
+'use client';
+
+import Loading from '@/components/common/Loading';
+import { useGetRandomKeywords } from '@/hooks/queries/useKeyword';
+import { useRequesterInfo } from '@/hooks/queries/useRequesterInfo';
+import Image from 'next/image';
+import { useParams } from 'next/navigation';
+import React from 'react';
+
+const KeywordCard = () => {
+ const params = useParams();
+ const uuid = String(params.uuid);
+
+ const { data: requesterInfo, isFetching } = useRequesterInfo(uuid);
+ const { data: randomKeywords, isFetching: isFetchingKeywords } = useGetRandomKeywords(uuid);
+
+ const hasKeywords = randomKeywords?.keywords && randomKeywords.keywords.length > 0;
+
+ return (
+
+
+
+ {isFetching || isFetchingKeywords ? (
+
+ ) : (
+ <>
+ {hasKeywords ? (
+ <>
+
+
{requesterInfo?.requesterName}๋์ ํํผ์
๋ก
+
{`ํํผ์
๋ก์ ๋ค๋ฅธ ๋๋ฃ๋ค์ด\n์ํํธ์คํฌ ๊ธฐ๋ฐ์ผ๋ก ์์ฑํด์ค ํค์๋์
๋๋ค`}
+
+
+ {randomKeywords.keywords.map((item, idx) => (
+
+ ))}
+
+ >
+ ) : (
+
+
+
+
{requesterInfo?.requesterName}๋์ ํํผ์
๋ก
+
{`์์ง ์ํํธ์คํฌ ํค์๋๊ฐ ์์ด์!\n${requesterInfo?.requesterName}๋์ ํํผ์
๋ก์ ์์ฑํด ํค์๋๋ฅผ ์ถ๊ฐํด์ฃผ์ธ์`}
+
+
+ )}
+ >
+ )}
+
+ );
+};
+
+export default KeywordCard;
diff --git a/src/app/(main)/teampsylog/head/[uuid]/page.tsx b/src/app/(main)/teampsylog/head/[uuid]/page.tsx
new file mode 100644
index 00000000..74beb20b
--- /dev/null
+++ b/src/app/(main)/teampsylog/head/[uuid]/page.tsx
@@ -0,0 +1,23 @@
+import React from 'react';
+import KeywordCard from './_components/KeywordCard';
+import Button from '@/components/common/Button';
+import Link from 'next/link';
+
+type Props = {
+ params: Promise<{ uuid: string }>;
+};
+
+const page = async ({ params }: Props) => {
+ const { uuid } = await params;
+
+ return (
+ <>
+
+
+
+
+ >
+ );
+};
+
+export default page;
diff --git a/src/app/(main)/teampsylog/page.tsx b/src/app/(main)/teampsylog/page.tsx
new file mode 100644
index 00000000..3e887031
--- /dev/null
+++ b/src/app/(main)/teampsylog/page.tsx
@@ -0,0 +1,13 @@
+import MobileHeader from '@/components/common/MobileHeader';
+import KeywordPage from './_components/KeywordPage';
+
+const Page = () => {
+ return (
+
+ );
+};
+
+export default Page;
diff --git a/src/app/favicon.ico b/src/app/favicon.ico
new file mode 100644
index 00000000..718d6fea
Binary files /dev/null and b/src/app/favicon.ico differ
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 00000000..aef22c49
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,277 @@
+@import 'tailwindcss';
+@import '../styles/_variables.scss';
+@import '../styles/_keyframe-animations.scss';
+
+@theme {
+ --breakpoint-*: initial;
+ --breakpoint-tablet: 640px;
+ --breakpoint-desktop: 1024px;
+ --breakpoint-wide: 1440px;
+}
+
+:root {
+ --background: #ffffff;
+ --foreground: #191919;
+}
+
+input:focus,
+select:focus {
+ outline: none !important;
+ box-shadow: none !important;
+}
+
+input {
+ &::placeholder {
+ color: var(--color-gray-500);
+ }
+ & {
+ color: var(--color-gray-800);
+ }
+}
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-pretendard: var(--font-pretendard);
+}
+
+@theme {
+ --color-gray-0: #ffffff;
+ --color-gray-50: #fdfdfd;
+ --color-gray-100: #fafafa;
+ --color-gray-200: #f2f2f2;
+ --color-gray-300: #e8e8e8;
+ --color-gray-400: #d9d9d9;
+ --color-gray-500: #c4c4c4;
+ --color-gray-600: #a7a7a7;
+ --color-gray-700: #5f5f5f;
+ --color-gray-800: #3b3b3b;
+ --color-gray-900: #191919;
+
+ --color-green-50: #e8fcf7;
+ --color-green-100: #d1faf0;
+ --color-green-200: #bbf7e8;
+ --color-green-300: #a3f5e0;
+ --color-green-400: #8bf3d9;
+ --color-green-500: #75f0d1;
+ --color-green-600: #5eedc9;
+ --color-green-700: #46ebc2;
+ --color-green-800: #19e6b3;
+ --color-green-900: #11dca9;
+
+ --color-yellow-900: #fbfcd4;
+
+ --color-red-10: #ffdfe1;
+ --color-red-100: #f1000d;
+ --color-red-200: #ce030f;
+
+ --color-blue-50: #d5f0ff;
+ --color-blue-200: #007df1;
+
+ --color-primary-50: var(--color-green-50);
+ --color-primary-100: var(--color-green-100);
+ --color-primary-200: var(--color-green-200);
+ --color-primary-300: var(--color-green-300);
+ --color-primary-400: var(--color-green-400);
+ --color-primary-500: var(--color-green-500);
+ --color-primary-600: var(--color-green-600);
+ --color-primary-700: var(--color-green-700);
+ --color-primary-800: var(--color-green-800);
+ --color-primary-900: var(--color-green-900);
+
+ --color-secondary: var(--color-yellow-900);
+}
+
+@media (prefers-color-scheme: dark) {
+ :root {
+ --background: #ffffff;
+ --foreground: #191919;
+ }
+}
+
+body {
+ background: var(--background);
+ color: var(--foreground);
+ font-family: var(--font-pretendard), Arial, Helvetica, sans-serif;
+}
+
+:root {
+ --font-weight-normal: 400;
+ --font-weight-medium: 500;
+ --font-weight-semibold: 600;
+
+ --letter-spacing: -0.02em;
+ --line-height: 1.5em;
+}
+
+@utility title-1 {
+ font-size: 28px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility title-2 {
+ font-size: 24px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility title-3 {
+ font-size: 20px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility title-4 {
+ font-size: 18px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-1 {
+ font-size: 20px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-2 {
+ font-size: 20px;
+ font-weight: var(--font-weight-medium);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-3 {
+ font-size: 18px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-4 {
+ font-size: 18px;
+ font-weight: var(--font-weight-medium);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-5 {
+ font-size: 16px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-6 {
+ font-size: 16px;
+ font-weight: var(--font-weight-medium);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-7 {
+ font-size: 14px;
+ font-weight: var(--font-weight-semibold);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-8 {
+ font-size: 14px;
+ font-weight: var(--font-weight-medium);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-9 {
+ font-size: 12px;
+ font-weight: var(--font-weight-medium);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-10 {
+ font-size: 12px;
+ font-weight: var(--font-weight-normal);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-11 {
+ font-size: 10px;
+ font-weight: var(--font-weight-medium);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+@utility body-12 {
+ font-size: 10px;
+ font-weight: var(--font-weight-normal);
+ letter-spacing: var(--letter-spacing);
+ line-height: var(--line-height);
+}
+
+/* Gradients */
+@utility gradient-bg-01 {
+ background: linear-gradient(90deg, #19e6b3 0%, #e9fbf6 100%);
+}
+
+@utility gradient-bg-02 {
+ background: linear-gradient(90deg, #19e6b3 0%, #fbfcd4 100%);
+}
+
+/* tiptap */
+.ProseMirror h1 {
+ @apply my-2 text-4xl font-bold;
+}
+.ProseMirror h2 {
+ @apply my-2 text-3xl font-bold;
+}
+.ProseMirror h3 {
+ @apply my-2 text-2xl font-bold;
+}
+.ProseMirror p.is-editor-empty:first-child::before {
+ content: attr(data-placeholder);
+ float: left;
+ color: #c4c4c4;
+ pointer-events: none;
+ height: 0;
+ white-space: pre-wrap;
+}
+.prose h1 {
+ @apply desktop:text-3xl desktop:my-2 my-1 text-2xl font-bold;
+}
+.prose h2 {
+ @apply desktop:text-2xl desktop:my-2 my-1 text-xl font-bold;
+}
+.prose h3 {
+ @apply desktop:text-xl desktop:my-1 my-0.5 text-lg font-bold;
+}
+.prose p {
+ @apply desktop:body-6 body-8;
+}
+
+.prose code {
+ @apply rounded bg-gray-100 px-1 py-0.5;
+ white-space: pre-wrap;
+ word-break: break-all;
+}
+
+/* slide-up ์ ๋๋ฉ์ด์
*/
+@keyframes slide-up {
+ from {
+ transform: translateY(100%);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
+
+.animate-slide-up {
+ animation: slide-up 0.3s ease-out;
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 00000000..7c6dbd15
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,39 @@
+import type { Metadata } from 'next';
+import localFont from 'next/font/local';
+import './globals.css';
+import { ModalProvider } from '@/contexts/ModalContext';
+import Providers from './provider';
+import { ToastProvider } from '@/contexts/ToastContext';
+
+const pretendard = localFont({
+ src: '../../public/fonts/PretendardVariable.woff2',
+ display: 'swap',
+ weight: '100 900',
+ variable: '--font-pretendard',
+});
+
+export const metadata: Metadata = {
+ title: 'TEAMFICIAL',
+ description: 'Generated by create next app',
+ icons: {
+ icon: '/favicon.svg',
+ },
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ );
+}
diff --git a/src/app/provider.tsx b/src/app/provider.tsx
new file mode 100644
index 00000000..96e5e4ff
--- /dev/null
+++ b/src/app/provider.tsx
@@ -0,0 +1,14 @@
+'use client';
+
+import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
+import { ReactNode, useState } from 'react';
+
+export default function Providers({ children }: { children: ReactNode }) {
+ const [queryClient] = useState(
+ () =>
+ new QueryClient(),
+ // TODO: queryClient ์ค์ ์ถ๊ฐ
+ );
+
+ return {children};
+}
diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx
new file mode 100644
index 00000000..de979bdb
--- /dev/null
+++ b/src/components/common/Button.tsx
@@ -0,0 +1,50 @@
+import React from 'react';
+
+type ButtonSize = 'small' | 'large';
+type ButtonVariant = 'primary' | 'red' | 'gray';
+
+type ButtonProps = {
+ label: string;
+ onClick?: () => void;
+ size?: ButtonSize;
+ variant?: ButtonVariant;
+ className?: string;
+ disabled?: boolean;
+ type?: 'button' | 'submit' | 'reset';
+};
+
+const Button = ({
+ label,
+ onClick,
+ size = 'large',
+ variant = 'primary',
+ className = '',
+ disabled = false,
+ type = 'button',
+}: ButtonProps) => {
+ const variantStyles: Record = {
+ primary: 'bg-primary-900 text-gray-0 enabled:hover:bg-primary-700',
+ red: 'bg-red-100 text-gray-0 enabled:hover:bg-red-200',
+ gray: 'bg-gray-300 text-gray-800 disabled:text-gray-600',
+ };
+
+ const sizeStyles: Record = {
+ small: 'body-7 desktop:body-5',
+ large: 'body-5 desktop:body-3',
+ };
+
+ const disabledStyle = 'opacity-50';
+
+ return (
+
+ );
+};
+
+export default Button;
diff --git a/src/components/common/DefaultToast.tsx b/src/components/common/DefaultToast.tsx
new file mode 100644
index 00000000..bbcca2c4
--- /dev/null
+++ b/src/components/common/DefaultToast.tsx
@@ -0,0 +1,7 @@
+export default function DefaultToast({ message }: { message: string }) {
+ return (
+
+ {message}
+
+ );
+}
diff --git a/src/components/common/DropdownSmall.tsx b/src/components/common/DropdownSmall.tsx
new file mode 100644
index 00000000..f468aa08
--- /dev/null
+++ b/src/components/common/DropdownSmall.tsx
@@ -0,0 +1,163 @@
+'use client';
+
+import Image from 'next/image';
+import { useEffect, useMemo, useRef, useState } from 'react';
+
+type Option = { label: string; value: string };
+type DropdownProps = {
+ name?: string;
+ value?: string;
+ onChange?: (v: string) => void;
+ placeholder?: string;
+ options: Option[];
+ className?: string;
+ required?: boolean;
+};
+
+const DropdownSmall = ({
+ name,
+ value = '',
+ onChange,
+ placeholder = '',
+ options,
+ className = '',
+ required,
+}: DropdownProps) => {
+ const [open, setOpen] = useState(false);
+ const [openWidth, setOpenWidth] = useState(null);
+
+ const isPlaceholder = !value;
+ const wrapRef = useRef(null);
+ const buttonRef = useRef(null);
+ const menuRef = useRef(null);
+ const measureRef = useRef(null);
+
+ const selectedLabel = useMemo(
+ () => options.find((o) => o.value === value)?.label ?? '',
+ [options, value],
+ );
+
+ // ๋ฐ๊นฅ ํด๋ฆญ ๋ซ๊ธฐ
+ useEffect(() => {
+ const onDocClick = (e: MouseEvent) => {
+ if (!wrapRef.current) return;
+ if (!wrapRef.current.contains(e.target as Node)) setOpen(false);
+ };
+ document.addEventListener('mousedown', onDocClick);
+ return () => document.removeEventListener('mousedown', onDocClick);
+ }, []);
+
+ useEffect(() => {
+ if (!open) {
+ setOpenWidth(null);
+ return;
+ }
+
+ const measureWidth = () => {
+ const btnW = buttonRef.current?.getBoundingClientRect().width ?? 0;
+ const measW = measureRef.current?.getBoundingClientRect().width ?? 0;
+ const menuW = menuRef.current?.getBoundingClientRect().width ?? 0;
+ setOpenWidth(Math.max(btnW, measW, menuW));
+ };
+
+ measureWidth();
+ window.addEventListener('resize', measureWidth);
+ return () => window.removeEventListener('resize', measureWidth);
+ }, [open, options, selectedLabel, placeholder]);
+
+ return (
+
+
+
+ {open && (
+
+ {options.map((opt, idx) => {
+ const isSelected = opt.value === value;
+ return (
+ - {
+ onChange?.(opt.value);
+ setOpen(false);
+ }}
+ >
+ {opt.label}
+
+ );
+ })}
+
+ )}
+
+ {/* ์ต๋ ๋๋น ์ธก์ ์ฉ */}
+
+
+ {isPlaceholder ? placeholder : selectedLabel}
+ {/* ์์ด์ฝ ๊ณต๊ฐ(24px + margin-left 1.25) ๋ฐ์ */}
+
+
+
+ {options.map((o) => (
+
+ {o.label}
+
+ ))}
+
+
+
+ {name && (
+
+ )}
+
+ );
+};
+
+export default DropdownSmall;
diff --git a/src/components/common/Error.tsx b/src/components/common/Error.tsx
new file mode 100644
index 00000000..a6503efa
--- /dev/null
+++ b/src/components/common/Error.tsx
@@ -0,0 +1,22 @@
+import Image from 'next/image';
+
+interface ErrorProps {
+ message?: string;
+}
+
+const ErrorDisplay = ({ message = '์! ์๋ฌ๊ฐ ๋ฐ์ํ์ด์' }: ErrorProps) => {
+ return (
+
+ );
+};
+
+export default ErrorDisplay;
diff --git a/src/components/common/ErrorToast.tsx b/src/components/common/ErrorToast.tsx
new file mode 100644
index 00000000..e6bcca93
--- /dev/null
+++ b/src/components/common/ErrorToast.tsx
@@ -0,0 +1,22 @@
+import Image from 'next/image';
+
+export default function ErrorToast({ title, message }: { title?: string; message: string }) {
+ return (
+ <>
+
+
+ >
+ );
+}
diff --git a/src/components/common/Footer.tsx b/src/components/common/Footer.tsx
new file mode 100644
index 00000000..5f3a2ae8
--- /dev/null
+++ b/src/components/common/Footer.tsx
@@ -0,0 +1,88 @@
+import Image from 'next/image';
+
+const Footer = () => {
+ return (
+ <>
+
+
+ >
+ );
+};
+
+export default Footer;
diff --git a/src/components/common/Header.tsx b/src/components/common/Header.tsx
new file mode 100644
index 00000000..85cef86e
--- /dev/null
+++ b/src/components/common/Header.tsx
@@ -0,0 +1,218 @@
+'use client';
+
+import Image from 'next/image';
+import Link from 'next/link';
+import React, { useEffect, useState } from 'react';
+import { useUserStore } from '@/store/useUserStore';
+import { usePathname, useRouter } from 'next/navigation';
+import clsx from 'clsx';
+import { useToast } from '@/contexts/ToastContext';
+import { isLoggedIn as checkIsLoggedIn } from '@/utils/auth';
+import Footer from './Footer';
+
+const Header = () => {
+ const { userName, _hasHydrated } = useUserStore();
+ const router = useRouter();
+
+ const [isLoggedIn, setIsLoggedIn] = useState(false);
+
+ useEffect(() => {
+ setIsLoggedIn(checkIsLoggedIn(userName));
+ }, [userName, _hasHydrated]);
+
+ useEffect(() => {
+ if (!_hasHydrated) return;
+ if (!checkIsLoggedIn(userName)) {
+ useUserStore.getState().clearUser();
+ setIsLoggedIn(false);
+ }
+ }, [_hasHydrated, userName, router]);
+
+ const pathname = usePathname();
+ const [isMenuOpen, setIsMenuOpen] = useState(false);
+ const { addToast } = useToast();
+
+ const [isProfileDropdownOpen, setIsProfileDropdownOpen] = useState(false);
+
+ useEffect(() => {
+ setIsProfileDropdownOpen(false);
+ }, [pathname]);
+
+ const handleProfileClick = () => {
+ setIsProfileDropdownOpen((prev) => !prev);
+ };
+
+ const handleLogout = () => {
+ useUserStore.getState().clearUser();
+ setIsProfileDropdownOpen(false);
+ addToast({ message: '๋ก๊ทธ์์ ์๋ฃ๋์์ต๋๋ค.' });
+ };
+
+ return (
+ <>
+ {/* PC */}
+
+
+
+
+
+
+ ํ๋ก์ ํธ
+
+
+ ํํผ์
๋ก
+
+
+ {isLoggedIn ? (
+
+
+ {isProfileDropdownOpen && (
+
+ setIsProfileDropdownOpen(false)}
+ >
+ ๋ง์ดํ์ด์ง
+
+
+
+ ์ ์ํ๊ธฐ
+
+
+ )}
+
+ ) : (
+
+ ๋ก๊ทธ์ธ/ํ์๊ฐ์
+
+
+ )}
+
+ {/* mobile */}
+
+
+
+
+
+
+ {/* ๋ชจ๋ฐ์ผ ๋ฉ๋ด ์ค๋ฒ๋ ์ด */}
+ {isMenuOpen && (
+
+ {/* ๋ฐฐ๊ฒฝ ์ค๋ฒ๋ ์ด */}
+
setIsMenuOpen(false)} />
+ {/* ๋ฉ๋ด ํจ๋*/}
+
+
+
+
+
+
+ {isLoggedIn ? `${userName}๋` : '๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค'}
+
+ {isLoggedIn ? (
+
+ ) : (
+
setIsMenuOpen(false)}
+ >
+ ๋ก๊ทธ์ธ/ํ์๊ฐ์
+
+ )}
+
+
+
+
+
+
+
+ setIsMenuOpen(false)}
+ >
+ ํ๋ก์ ํธ
+
+ setIsMenuOpen(false)}
+ >
+ ํํผ์
๋ก
+
+ {isLoggedIn ? (
+ setIsMenuOpen(false)}
+ >
+ ๋ง์ดํ์ด์ง
+
+ ) : null}
+
+
+
+
+
+
+
+ )}
+ >
+ );
+};
+
+export default Header;
diff --git a/src/components/common/Loading.tsx b/src/components/common/Loading.tsx
new file mode 100644
index 00000000..30cad4f0
--- /dev/null
+++ b/src/components/common/Loading.tsx
@@ -0,0 +1,23 @@
+import Image from 'next/image';
+
+const Loading = () => {
+ return (
+
+
+
+
+
๋ก๋ฉ ์ค
+
์ ์๋ง ๊ธฐ๋ค๋ ค์ฃผ์ธ์
+
+
+
+ );
+};
+
+export default Loading;
diff --git a/src/components/common/MobileHeader.tsx b/src/components/common/MobileHeader.tsx
new file mode 100644
index 00000000..74a40f2d
--- /dev/null
+++ b/src/components/common/MobileHeader.tsx
@@ -0,0 +1,46 @@
+'use client';
+
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+
+interface MobileHeaderProps {
+ title: string;
+ progress?: number;
+}
+
+const MobileHeader = ({ title, progress }: MobileHeaderProps) => {
+ const router = useRouter();
+
+ return (
+ <>
+
+
+
+
+
+
+
+ {title}
+
+
+
+
+
+ {/* ์งํ ๋ฐ */}
+ {typeof progress === 'number' ? (
+
+ ) : (
+
+ )}
+ >
+ );
+};
+
+export default MobileHeader;
diff --git a/src/components/common/Pagination.tsx b/src/components/common/Pagination.tsx
new file mode 100644
index 00000000..4ba88160
--- /dev/null
+++ b/src/components/common/Pagination.tsx
@@ -0,0 +1,83 @@
+'use client';
+
+import Image from 'next/image';
+
+type PaginationProps = {
+ totalPages: number;
+ currentPage: number;
+ onPageChange?: (page: number) => void;
+};
+
+const Pagination: React.FC
= ({ totalPages, currentPage, onPageChange }) => {
+ const page = currentPage;
+
+ const handleChange = (newPage: number) => {
+ if (newPage < 1 || newPage > totalPages) return;
+ onPageChange?.(newPage);
+ };
+
+ const groupSize = 5;
+ const currentGroup = Math.ceil(page / groupSize);
+ const startPage = (currentGroup - 1) * groupSize + 1;
+ const endPage = Math.min(startPage + groupSize - 1, totalPages);
+ const visiblePages = Array.from({ length: endPage - startPage + 1 }, (_, i) => startPage + i);
+ const pagesToRender = visiblePages.length > 0 ? visiblePages : [1];
+
+ return (
+
+
+
+
+
+
+
+ {pagesToRender.map((num) => (
+
+ ))}
+
+
+
+
+
+
+
+ );
+};
+
+export default Pagination;
diff --git a/src/components/common/SuccessToast.tsx b/src/components/common/SuccessToast.tsx
new file mode 100644
index 00000000..595c6704
--- /dev/null
+++ b/src/components/common/SuccessToast.tsx
@@ -0,0 +1,7 @@
+export default function SuccessToast({ message }: { message: string }) {
+ return (
+
+ {message}
+
+ );
+}
diff --git a/src/components/common/Tag.tsx b/src/components/common/Tag.tsx
new file mode 100644
index 00000000..a74b75ba
--- /dev/null
+++ b/src/components/common/Tag.tsx
@@ -0,0 +1,22 @@
+import { cn } from '@/utils/cn';
+
+interface TagProps {
+ children: React.ReactNode;
+ className?: string;
+}
+
+export const Tag = ({ children, className = '' }: TagProps) => {
+ return (
+
+ {children}
+
+ );
+};
+
+export const Keyword = ({ children, className = '' }: TagProps) => {
+ return (
+
+ {children}
+
+ );
+};
diff --git a/src/components/common/ToastContainer.tsx b/src/components/common/ToastContainer.tsx
new file mode 100644
index 00000000..4830d9c1
--- /dev/null
+++ b/src/components/common/ToastContainer.tsx
@@ -0,0 +1,12 @@
+'use client';
+
+import { ToastMessage } from '@/contexts/ToastContext';
+import DefaultToast from './DefaultToast';
+import ErrorToast from './ErrorToast';
+import SuccessToast from './SuccessToast';
+
+export default function ToastContainer({ toast }: { toast: ToastMessage }) {
+ if (toast.type === 'success') return ;
+ if (toast.type === 'error') return ;
+ return ;
+}
diff --git a/src/components/common/Toggle.tsx b/src/components/common/Toggle.tsx
new file mode 100644
index 00000000..a00a7644
--- /dev/null
+++ b/src/components/common/Toggle.tsx
@@ -0,0 +1,39 @@
+import React from 'react';
+
+type ToggleProps = {
+ checked: boolean;
+ onChange: (v: boolean) => void;
+ label?: string;
+ disabled?: boolean;
+};
+
+const Toggle: React.FC = ({ checked, onChange, label, disabled }) => {
+ return (
+
+ );
+};
+
+export default Toggle;
diff --git a/src/components/common/button/Button.tsx b/src/components/common/button/Button.tsx
new file mode 100644
index 00000000..5896a6a9
--- /dev/null
+++ b/src/components/common/button/Button.tsx
@@ -0,0 +1,15 @@
+import { cn } from '@/utils/cn';
+
+interface ButtonProps extends React.ButtonHTMLAttributes {
+ className?: string;
+}
+
+const Button = ({ children, className, ...props }: ButtonProps) => {
+ return (
+
+ );
+};
+
+export default Button;
diff --git a/src/components/common/button/LineButton.tsx b/src/components/common/button/LineButton.tsx
new file mode 100644
index 00000000..775c56a6
--- /dev/null
+++ b/src/components/common/button/LineButton.tsx
@@ -0,0 +1,18 @@
+interface LineButtonProps {
+ children: React.ReactNode;
+ onClick: () => void;
+}
+
+const LineButton = ({ children, onClick }: LineButtonProps) => {
+ return (
+
+ );
+};
+
+export default LineButton;
diff --git a/src/components/layout/MobileOverlayWrapper.tsx b/src/components/layout/MobileOverlayWrapper.tsx
new file mode 100644
index 00000000..fb2e2809
--- /dev/null
+++ b/src/components/layout/MobileOverlayWrapper.tsx
@@ -0,0 +1,52 @@
+'use client';
+
+import { Suspense } from 'react';
+import { usePathname } from 'next/navigation';
+import { useIsMobile } from '@/hooks/useIsMobile';
+import { ReactNode } from 'react';
+
+// ์ค๋ฒ๋ ์ด ๊ฒฝ๋ก ํจํด ์ ์
+const OVERLAY_ROUTES = [
+ /^\/question\/[^/]+$/, // /question/:uuid
+ /^\/teampsylog\/[^/]+$/, // /teampsylog/:uuid
+ /^\/teampsylog$/, // /teampsylog
+ /^\/recruit$/, // /recruit
+ /^\/mypage\/profile$/, // /mypage/profile
+ /^\/mypage\/profile\/edit\/[^/]+$/, // /mypage/profile/edit/:profileId
+ /^\/project\/all$/, // /project/all
+ /^\/mypage\/applicant$/, // /mypage/applicant
+ /^\/mypage\/applied$/, // /mypage/applied
+ /^\/mypage\/team$/, // /mypage/team
+ /^\/mypage\/team\/[^/]+$/, // /mypage/team/:id
+ /^\/project\/[^/]+\/edit$/, // /project/:id/edit
+ /^\/project\/[^/]+\/applicants$/, // /project/:id/applicants
+];
+
+interface MobileOverlayWrapperProps {
+ children: ReactNode;
+}
+
+function MobileOverlayWrapperContent({ children }: MobileOverlayWrapperProps) {
+ const pathname = usePathname();
+ const isMobile = useIsMobile();
+
+ const shouldShowOverlay = isMobile && OVERLAY_ROUTES.some((pattern) => pattern.test(pathname));
+
+ if (shouldShowOverlay) {
+ return (
+
+ );
+ }
+
+ return <>{children}>;
+}
+
+export default function MobileOverlayWrapper({ children }: MobileOverlayWrapperProps) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/src/components/login/LoginSocial.tsx b/src/components/login/LoginSocial.tsx
new file mode 100644
index 00000000..ff330728
--- /dev/null
+++ b/src/components/login/LoginSocial.tsx
@@ -0,0 +1,39 @@
+import Image from 'next/image';
+import { socialConfig } from './LoginSocialList';
+
+type SocialType = 'kakao' | 'naver' | 'google';
+
+interface LoginSocialProps {
+ type: SocialType;
+ onClick?: () => void;
+ isRecentLogin?: boolean;
+}
+
+export default function LoginSocial({ type, onClick, isRecentLogin = false }: LoginSocialProps) {
+ const config = socialConfig[type];
+
+ return (
+
+ {isRecentLogin && (
+
+
+
+ )}
+
+
+
+
+
{config.name} ๊ณ์ ์ผ๋ก ๊ณ์ํ๊ธฐ
+
+
+ );
+}
diff --git a/src/components/login/LoginSocialList.tsx b/src/components/login/LoginSocialList.tsx
new file mode 100644
index 00000000..45e4cfe5
--- /dev/null
+++ b/src/components/login/LoginSocialList.tsx
@@ -0,0 +1,89 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import LoginSocial from './LoginSocial';
+import { getCookie } from '@/utils/cookie';
+
+type SocialType = 'kakao' | 'naver' | 'google';
+
+function handleKakaoLogin() {
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ localStorage.removeItem('user');
+ const REST_API_KEY = process.env.NEXT_PUBLIC_KAKAO_CLIENT_ID;
+ const KAKAO_REDIRECT_URI = `${process.env.NEXT_PUBLIC_KAKAO_REDIRECT_URI}`;
+ const kakaoUrl = `https://kauth.kakao.com/oauth/authorize?client_id=${REST_API_KEY}&redirect_uri=${KAKAO_REDIRECT_URI}&response_type=code`;
+ window.location.replace(kakaoUrl);
+}
+function handleGoogleLogin() {
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ localStorage.removeItem('user');
+ const REST_API_KEY = process.env.NEXT_PUBLIC_GOOGLE_CLIENT_ID;
+ const GOOGLE_REDIRECT_URI = `${process.env.NEXT_PUBLIC_GOOGLE_REDIRECT_URI}`;
+ const googleUrl = `https://accounts.google.com/o/oauth2/v2/auth?client_id=${REST_API_KEY}&redirect_uri=${GOOGLE_REDIRECT_URI}&response_type=code&scope=https://www.googleapis.com/auth/userinfo.email https://www.googleapis.com/auth/userinfo.profile`;
+ window.location.replace(googleUrl);
+}
+function handleNaverLogin() {
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ localStorage.removeItem('user');
+ const REST_API_KEY = process.env.NEXT_PUBLIC_NAVER_CLIENT_ID;
+ const NAVER_STATE = process.env.NEXT_PUBLIC_NAVER_STATE;
+ const NAVER_REDIRECT_URI = `${process.env.NEXT_PUBLIC_NAVER_REDIRECT_URI}`;
+ const naverUrl = `https://nid.naver.com/oauth2.0/authorize?response_type=code&client_id=${REST_API_KEY}&state=${NAVER_STATE}&redirect_uri=${NAVER_REDIRECT_URI}`;
+ window.location.replace(naverUrl);
+}
+
+export const socialConfig = {
+ naver: {
+ name: '๋ค์ด๋ฒ',
+ imageSrc: '/icons/naver.svg',
+ bgColor: 'bg-[#05CB5B]',
+ textColor: 'text-gray-0',
+ onClick: handleNaverLogin,
+ },
+ kakao: {
+ name: '์นด์นด์ค',
+ imageSrc: '/icons/kakao.svg',
+ bgColor: 'bg-[#FEE500]',
+ textColor: 'text-gray-800',
+ onClick: handleKakaoLogin,
+ },
+ google: {
+ name: '๊ตฌ๊ธ',
+ imageSrc: '/icons/google.svg',
+ bgColor: 'bg-gray-200',
+ textColor: 'text-gray-800',
+ onClick: handleGoogleLogin,
+ },
+};
+
+export default function LoginSocialList() {
+ const [lastLoginProvider, setLastLoginProvider] = useState(null);
+
+ useEffect(() => {
+ if (typeof window !== 'undefined') {
+ const lastProvider = getCookie('lastLoginProvider') as SocialType | null;
+ if (lastProvider && ['naver', 'kakao', 'google'].includes(lastProvider)) {
+ setLastLoginProvider(lastProvider);
+ }
+ }
+ }, []);
+
+ // ๊ณ ์ ๋ ์์: ๋ค์ด๋ฒ, ์นด์นด์ค, ๊ตฌ๊ธ
+ const fixedOrder: SocialType[] = ['naver', 'kakao', 'google'];
+
+ return (
+
+ {fixedOrder.map((type) => (
+
+ ))}
+
+ );
+}
diff --git a/src/components/modal/ApplicantFinishModal.tsx b/src/components/modal/ApplicantFinishModal.tsx
new file mode 100644
index 00000000..0738ccc4
--- /dev/null
+++ b/src/components/modal/ApplicantFinishModal.tsx
@@ -0,0 +1,45 @@
+'use client';
+
+import { BaseModalProps } from '@/constants/ModalList';
+import Button from '../common/button/Button';
+import BaseModal from './index';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+
+// ํ์ ๋ชจ์ง์ ๋ง์น ๊ฒ์ ๋ชจ๋ฌ2
+const ApplicantFinishModal = ({ isOpen, onClose }: BaseModalProps) => {
+ const router = useRouter();
+
+ const handleFinishClick = () => {
+ onClose();
+ router.push('/mypage/');
+ };
+
+ return (
+
+
+
+
ํ์ ๋ชจ์ง์ด ์๋ฃ๋์์ด์!
+
+ {`๋ง์ดํ์ด์ง > ์ง์์ ํํฉ์์ ํ์์ ๋ณด๋ฅผ ํ์ธํ ์ ์์ด์`}
+
+
+
+
+
+
+ );
+};
+
+export default ApplicantFinishModal;
diff --git a/src/components/modal/ApplicateFinishModal.tsx b/src/components/modal/ApplicateFinishModal.tsx
new file mode 100644
index 00000000..e1d2d890
--- /dev/null
+++ b/src/components/modal/ApplicateFinishModal.tsx
@@ -0,0 +1,66 @@
+'use client';
+
+import { ApplicateFinishModalProps } from '@/constants/ModalList';
+import Button from '../common/button/Button';
+import BaseModal from './index';
+import Image from 'next/image';
+import { useCloseProject } from '@/hooks/mutation/useCloseProject';
+import { useModal } from '@/contexts/ModalContext';
+
+// ํ์ ๋ชจ์ง์ ๋ง์น ๊ฒ์ ๋ชจ๋ฌ1
+const ApplicateFinishModal = ({ isOpen, onClose, recruitingPostId }: ApplicateFinishModalProps) => {
+ const handleCancelClick = () => {
+ onClose();
+ };
+
+ const { openModal } = useModal();
+
+ const { mutate: closeProject } = useCloseProject();
+ if (!recruitingPostId) return null;
+
+ const handleFinishClick = () => {
+ closeProject(recruitingPostId, {
+ onSuccess: () => {
+ onClose();
+ openModal('applicantFinish');
+ },
+ onError: (error) => {
+ console.error('Failed to close project:', error);
+ },
+ });
+ };
+
+ return (
+
+
+
+
ํ์ ๋ชจ์ง์ ๋ง์น์๊ฒ ์ด์?
+
+ {`์ ๋ง ๋ชจ์ง์ ๋ง์น๊ฒ ์ต๋๊น? ์ง๊ธ ๋ชจ์ง์ ๋ง์น๋ฉด ๋๋๋ฆด ์ ์์ด์`}
+
+
+
+
+
+
+
+ );
+};
+
+export default ApplicateFinishModal;
diff --git a/src/components/modal/ApplyCompleteModal.tsx b/src/components/modal/ApplyCompleteModal.tsx
new file mode 100644
index 00000000..12847d5c
--- /dev/null
+++ b/src/components/modal/ApplyCompleteModal.tsx
@@ -0,0 +1,60 @@
+'use client';
+
+import { ApplyCompleteModalProps } from '@/constants/ModalList';
+import Button from '../common/button/Button';
+import BaseModal from './index';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+import { useQueryClient } from '@tanstack/react-query';
+
+const ApplyCompleteModal = ({ isOpen, onClose, postId }: ApplyCompleteModalProps) => {
+ const queryClient = useQueryClient();
+
+ const navigate = useRouter();
+
+ const handleClose = async () => {
+ if (postId != null) {
+ await queryClient.invalidateQueries({ queryKey: ['project', postId] });
+ }
+ onClose();
+ };
+
+ const handleClick = async () => {
+ await handleClose();
+ navigate.push('/mypage');
+ };
+
+ return (
+
+
+
+
์ง์์ด ์๋ฃ๋์์ด์!
+
+ ์ง์ํ ํ๋ก์ ํธ๋ ๋ง์ดํ์ด์ง์์ ํฉ๊ฒฉ์ฌ๋ถ๋ฅผ ์ ์ ์์ด์
+
+
+
+
+
+
+
+ );
+};
+
+export default ApplyCompleteModal;
diff --git a/src/components/modal/DeleteModal.tsx b/src/components/modal/DeleteModal.tsx
new file mode 100644
index 00000000..cf9084c0
--- /dev/null
+++ b/src/components/modal/DeleteModal.tsx
@@ -0,0 +1,68 @@
+'use client';
+
+import { DeleteModalProps } from '@/constants/ModalList';
+import Button from '../common/button/Button';
+import BaseModal from './index';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+import { useDeleteProject } from '@/hooks/mutation/useDeleteProject';
+
+const DeleteModal = ({ isOpen, onClose, postId, projectName }: DeleteModalProps) => {
+ const router = useRouter();
+ const { mutate: deleteProject } = useDeleteProject();
+
+ const handleDelete = () => {
+ if (!postId) return;
+
+ deleteProject(postId, {
+ onSuccess: () => {
+ onClose();
+ router.push('/project');
+ router.refresh();
+ },
+ onError: (error) => {
+ console.error('Failed to delete project:', error);
+ },
+ });
+ };
+
+ return (
+
+
+
+
์ ๋ง ์ญ์ ํ์๊ฒ ์ด์?
+
+ {`'${projectName}' ๊ฒ์๋ฌผ์ด ์๊ตฌ์ ์ผ๋ก ์ญ์ ๋ฉ๋๋ค`}
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DeleteModal;
diff --git a/src/components/modal/NotFinishModal.tsx b/src/components/modal/NotFinishModal.tsx
new file mode 100644
index 00000000..c284e2d8
--- /dev/null
+++ b/src/components/modal/NotFinishModal.tsx
@@ -0,0 +1,55 @@
+'use client';
+
+import { BaseModalProps } from '@/constants/ModalList';
+import BaseModal from './index';
+import Image from 'next/image';
+import Button from '../common/button/Button';
+
+interface NotFinishModalProps extends BaseModalProps {
+ onConfirm?: () => void;
+}
+
+const NotFinishModal = ({ isOpen, onClose, onConfirm }: NotFinishModalProps) => {
+ const handleLeave = () => {
+ if (onConfirm) {
+ onConfirm();
+ }
+ onClose();
+ };
+
+ return (
+
+
+
+
+ ์์ง ๋ชจ์ง๊ธ ์์ฑ์ด ์๋ฃ๋์ง ์์์ด์.
+
+
+ ์ง์ํ์ง ์๊ณ ๋๊ฐ๋ฉด ์์ฑํ ๋ด์ฉ์ด ์ฌ๋ผ์ ธ์
+
+
+
+
+
+
+
+ );
+};
+
+export default NotFinishModal;
diff --git a/src/components/modal/RecruitCompleteModal.tsx b/src/components/modal/RecruitCompleteModal.tsx
new file mode 100644
index 00000000..dd244717
--- /dev/null
+++ b/src/components/modal/RecruitCompleteModal.tsx
@@ -0,0 +1,57 @@
+'use client';
+
+import { RecruitCompleteModalProps } from '@/constants/ModalList';
+import Button from '../common/button/Button';
+import BaseModal from './index';
+import Image from 'next/image';
+
+const RecruitCompleteModal = ({
+ isOpen,
+ onClose,
+ onListClick,
+ onDetailClick,
+}: RecruitCompleteModalProps) => {
+ const handleListClick = () => {
+ onClose();
+ onListClick?.();
+ };
+
+ const handleDetailClick = () => {
+ onClose();
+ onDetailClick?.();
+ };
+
+ return (
+
+
+
+
ํ์ ๋ชจ์ง๊ธ ์์ฑ์ด ์๋ฃ๋์์ด์!
+
+ {`์์ฑํ ๊ธ์ ๋ง์ดํ์ด์ง > ์ง์์ ํํฉ์์ ํ์ธํ ์ ์์ด์`}
+
+
+
+
+
+
+
+ );
+};
+
+export default RecruitCompleteModal;
diff --git a/src/components/modal/ReportCommentModal.tsx b/src/components/modal/ReportCommentModal.tsx
new file mode 100644
index 00000000..5533909f
--- /dev/null
+++ b/src/components/modal/ReportCommentModal.tsx
@@ -0,0 +1,136 @@
+'use client';
+
+import BaseModal from './index';
+import MessageTextarea from './apply/MessageTextarea';
+
+import { useState } from 'react';
+import { ReportCommentModalProps } from '@/constants/ModalList';
+import { useModal } from '@/contexts/ModalContext';
+import QuestionDropdown from '@/app/(main)/question/[uuid]/_components/QuestionDropdown';
+import Button from '../common/Button';
+import { RequestReportComment } from '@/types/teampsylog';
+
+const REPORT_TYPE_OPTIONS = [
+ '๋น๋ฐฉ์ ์ธ ๋ด์ฉ์
๋๋ค',
+ '์ ํฉํ์ง ์์ ๋ด์ฉ์ ํค์๋์
๋๋ค',
+ '๊ธฐํ(์ง์ ์
๋ ฅ)',
+];
+
+const ReportCommentModal = ({ isOpen, onClose, keywordCommentId }: ReportCommentModalProps) => {
+ const { openModal } = useModal();
+ const [reportType, setReportType] = useState(null);
+ const [content, setContent] = useState('');
+ const [etcInput, setEtcInput] = useState('');
+ const [showEtc, setShowEtc] = useState(false);
+
+ const handleTypeChange = (value: string) => {
+ setReportType(value);
+ if (value === '๊ธฐํ(์ง์ ์
๋ ฅ)') {
+ setShowEtc(true);
+ } else {
+ setShowEtc(false);
+ setEtcInput('');
+ }
+ };
+
+ const handleContentChange = (value: string) => {
+ setContent(value);
+ };
+
+ const handleEtcInputChange = (e: React.ChangeEvent) => {
+ setEtcInput(e.target.value);
+ };
+
+ const getReportTypeCode = (type: string | null) => {
+ switch (type) {
+ case '๋น๋ฐฉ์ ์ธ ๋ด์ฉ์
๋๋ค':
+ return 'HATE_SPEECH';
+ case '์ ํฉํ์ง ์์ ๋ด์ฉ์ ํค์๋์
๋๋ค':
+ return 'UNSUITABLE_KEYWORD';
+ case '๊ธฐํ(์ง์ ์
๋ ฅ)':
+ return 'OTHER';
+ default:
+ return '';
+ }
+ };
+
+ const handleSubmitClick = () => {
+ if (keywordCommentId === undefined || keywordCommentId === null) {
+ console.warn('keywordCommentId๊ฐ ์ ์๋์ง ์์์ต๋๋ค.');
+ return;
+ }
+
+ const typeCode = getReportTypeCode(reportType);
+
+ const data: RequestReportComment = {
+ keywordCommentId,
+ reportType: typeCode as 'HATE_SPEECH' | 'UNSUITABLE_KEYWORD' | 'OTHER',
+ content,
+ ...(typeCode === 'OTHER' && etcInput.trim() && { reportEtc: etcInput.trim() }),
+ };
+
+ console.log('์ ๊ณ ๋ฐ์ดํฐ:', data);
+ openModal('reportComplete', { data });
+ };
+
+ const isSubmitDisabled =
+ content.length > 300 ||
+ keywordCommentId === undefined ||
+ keywordCommentId === null ||
+ !reportType ||
+ (reportType === '๊ธฐํ(์ง์ ์
๋ ฅ)' ? !etcInput.trim() : false) ||
+ !content.trim();
+
+ return (
+
+
+
+
+
+ ์ ๊ณ ์ ํ *
+
+
+ {showEtc && (
+
+ )}
+
+
+
+ ์ ๊ณ ๋ด์ฉ
+ *
+ >
+ }
+ placeholder="์ธ๋ถ๋ด์ฉ์ ์
๋ ฅํด์ฃผ์ธ์(300์ ์ด๋ด)"
+ value={content}
+ onChange={handleContentChange}
+ maxLength={300}
+ />
+
+
+
+
+
+ );
+};
+
+export default ReportCommentModal;
diff --git a/src/components/modal/ReportCompleteModal.tsx b/src/components/modal/ReportCompleteModal.tsx
new file mode 100644
index 00000000..fdcc80c9
--- /dev/null
+++ b/src/components/modal/ReportCompleteModal.tsx
@@ -0,0 +1,63 @@
+'use client';
+
+import { ReportCompleteModalProps } from '@/constants/ModalList';
+import Button from '../common/button/Button';
+import BaseModal from './index';
+import Image from 'next/image';
+import { usePostReportComment } from '@/hooks/mutation/useReportComment';
+import { useToast } from '@/contexts/ToastContext';
+
+const ReportCompleteModal = ({ isOpen, onClose, data }: ReportCompleteModalProps) => {
+ const { addToast } = useToast();
+ const handleCancelClick = () => {
+ onClose();
+ };
+
+ const { mutate: reportComment } = usePostReportComment();
+
+ const handleSubmitClick = () => {
+ reportComment(data, {
+ onSuccess: () => {
+ addToast({ message: '์ ๊ณ ๊ฐ ์ ์๋์์ต๋๋ค.' });
+ onClose();
+ },
+ onError: () => {
+ addToast({ type: 'error', message: '์ ๊ณ ์ ์์ ์คํจํ์ต๋๋ค. ๋ค์ ์๋ํด์ฃผ์ธ์.' });
+ },
+ });
+ };
+
+ return (
+
+
+
+
์ ๋ง ์ ๊ณ ํ ๊น์?
+
+ ์ ๊ณ ๋ด์ฉ์ด ํํผ์
์๊ฒ ์ ๋ฌ๋ฉ๋๋ค.
+
+
+
+
+
+
+
+ );
+};
+
+export default ReportCompleteModal;
diff --git a/src/components/modal/TeamPsylogAskModal.tsx b/src/components/modal/TeamPsylogAskModal.tsx
new file mode 100644
index 00000000..b12811b9
--- /dev/null
+++ b/src/components/modal/TeamPsylogAskModal.tsx
@@ -0,0 +1,90 @@
+import Image from 'next/image';
+import BaseModal from '.';
+import Button from '../common/button/Button';
+import { TeamPsylogAskModalProps } from '@/constants/ModalList';
+import { useModal } from '@/contexts/ModalContext';
+import { useTeamficialLog } from '@/hooks/mutation/useTeamficialLog';
+import { useToast } from '@/contexts/ToastContext';
+
+const TeamPsylogAskModal = ({
+ isOpen,
+ onClose,
+ userName,
+ uuid,
+ formData,
+}: TeamPsylogAskModalProps) => {
+ const { openModal } = useModal();
+ const { addToast } = useToast();
+ const { mutate: submitLog, isPending } = useTeamficialLog();
+
+ const handleClick = () => {
+ if (isPending) return;
+
+ const body = {
+ userUuid: uuid,
+ content1: formData.set1.answer,
+ content2: formData.set2.answer,
+ content3: formData.set3.answer,
+ };
+
+ submitLog(body, {
+ onSuccess: () => {
+ onClose();
+ openModal('teamPsylogComplete', { userName, uuid });
+ },
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ onError: (error: any) => {
+ const code = error?.response?.data?.code;
+
+ if (code === 'KEYWORD_COMMENT5001') {
+ onClose();
+ addToast({
+ type: 'error',
+ title: '์ด๋ฏธ ์์ฑ๋ ํํผ์
๋ก์ด ์์ด์!',
+ message: 'ํ ์ฌ๋์๊ฒ๋ ํ๋์ ํํผ์
๋ก๋ง ์ ๋ฌํ ์ ์์ด์',
+ });
+ return;
+ }
+ onClose();
+ addToast({ message: 'ํํผ์
๋ก ์์ฑ ์ค ๋ฌธ์ ๊ฐ ๋ฐ์ํ์ต๋๋ค.' });
+ },
+ });
+ };
+
+ return (
+
+
+
+
+ {userName}๋์ ํํผ์
๋ก ์์ฑ์ ์๋ฃํ ๊น์?
+
+
+
+
+
+
+
+ );
+};
+
+export default TeamPsylogAskModal;
diff --git a/src/components/modal/TeamPsylogCompleteModal.tsx b/src/components/modal/TeamPsylogCompleteModal.tsx
new file mode 100644
index 00000000..135c5b94
--- /dev/null
+++ b/src/components/modal/TeamPsylogCompleteModal.tsx
@@ -0,0 +1,57 @@
+import Button from '../common/button/Button';
+import BaseModal from './index';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+import { TeamPsylogCompleteModalProps } from '@/constants/ModalList';
+
+const TeamPsylogCompleteModal = ({
+ uuid,
+ userName,
+ isOpen,
+ onClose,
+}: TeamPsylogCompleteModalProps) => {
+ const router = useRouter();
+
+ const handleHome = () => {
+ router.push('/project');
+ onClose();
+ };
+
+ const handleTeampsylog = () => {
+ router.push(`/teampsylog/${uuid}`);
+ onClose();
+ };
+
+ return (
+
+
+
+
+ {userName}๋์ ํํผ์
๋ก ์์ฑ์ด ์๋ฃ๋์ด์
+
+
+
+
+
+
+
+ );
+};
+
+export default TeamPsylogCompleteModal;
diff --git a/src/components/modal/apply/ApplyModal.tsx b/src/components/modal/apply/ApplyModal.tsx
new file mode 100644
index 00000000..362d5143
--- /dev/null
+++ b/src/components/modal/apply/ApplyModal.tsx
@@ -0,0 +1,94 @@
+'use client';
+
+import Button from '@/components/common/button/Button';
+import BaseModal from '../index';
+import MessageTextarea from './MessageTextarea';
+import ProfileSlider from './profile/ProfileSlider';
+import { useModal } from '@/contexts/ModalContext';
+import { useState } from 'react';
+import { ApplyModalProps } from '@/constants/ModalList';
+import { useApplicateProject } from '@/hooks/mutation/useApplicateProject';
+import { PositionType } from '@/utils/position';
+import PartDropdown from './PartDropdown';
+import { useToast } from '@/contexts/ToastContext';
+
+const ApplyModal = ({ isOpen, onClose, postId, recruitingPositions }: ApplyModalProps) => {
+ const { mutate: applicateProject } = useApplicateProject();
+ const positions = recruitingPositions?.map((pos) => pos.position) || [];
+
+ const { openModal } = useModal();
+ const { addToast } = useToast();
+
+ const [selectedProfileId, setSelectedProfileId] = useState(null);
+ const [selectedPosition, setSelectedPosition] = useState(null);
+ const [message, setMessage] = useState('');
+
+ const handleProfileSelect = (profileId: number) => {
+ setSelectedProfileId(profileId);
+ };
+
+ const handleSubmit = () => {
+ if (!postId || !selectedProfileId || !selectedPosition) return;
+ if (message.length > 250) return;
+
+ const applicationData = {
+ recruitingPostId: postId,
+ profileId: selectedProfileId,
+ position: selectedPosition,
+ content: message,
+ };
+
+ applicateProject(applicationData, {
+ onSuccess: () => {
+ onClose();
+ openModal('applyComplete', { postId });
+ },
+ onError: (error) => {
+ console.error('Failed to apply for project:', error);
+ addToast({ message: 'ํ๋ก์ ํธ ์ง์์ ์คํจํ์ด์. ๋ค์ ์๋ํด์ฃผ์ธ์.', type: 'error' });
+ },
+ });
+ };
+
+ return (
+
+
+
+
+ ํด๋น ํ๋ก์ ํธ๋ฅผ ์ง์ํ ๊น์?
+ *
+
+
+ ๋ง์ดํ์ด์ง์์ ์ค์ ํ ํ๋กํ ์ค ํ ๊ฐ์ง๋ฅผ ์ ํํด์ฃผ์ธ์
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ApplyModal;
diff --git a/src/components/modal/apply/ApplyModalMo.tsx b/src/components/modal/apply/ApplyModalMo.tsx
new file mode 100644
index 00000000..349ffaf3
--- /dev/null
+++ b/src/components/modal/apply/ApplyModalMo.tsx
@@ -0,0 +1,155 @@
+'use client';
+
+import Button from '@/components/common/button/Button';
+import BaseModal from '../index';
+import MessageTextarea from './MessageTextarea';
+import { useModal } from '@/contexts/ModalContext';
+import { useCallback, useEffect, useState } from 'react';
+import { ApplyModalProps } from '@/constants/ModalList';
+import { useApplicateProject } from '@/hooks/mutation/useApplicateProject';
+import { PositionType } from '@/utils/position';
+import PartDropdown from './PartDropdown';
+import MoProfileSlider from './profile/MoProfileSlider';
+
+type Step = 'profile' | 'detail';
+
+const ApplyModalMo = ({ isOpen, onClose, postId, recruitingPositions }: ApplyModalProps) => {
+ const { mutate: applicateProject } = useApplicateProject();
+ const { openModal } = useModal();
+ const positions = recruitingPositions?.map((pos) => pos.position) || [];
+
+ const [step, setStep] = useState('profile');
+ const [selectedProfileId, setSelectedProfileId] = useState(null);
+ const [selectedPosition, setSelectedPosition] = useState(null);
+ const [message, setMessage] = useState('');
+
+ useEffect(() => {
+ if (!isOpen) {
+ setStep('profile');
+ setSelectedProfileId(null);
+ setSelectedPosition(null);
+ setMessage('');
+ }
+ }, [isOpen]);
+
+ const handleProfileSelect = useCallback((profileId: number) => {
+ setSelectedProfileId(profileId);
+ }, []);
+
+ const handleNext = () => {
+ if (selectedProfileId) {
+ setStep('detail');
+ }
+ };
+
+ const handleBack = () => {
+ setStep('profile');
+ };
+
+ const handleClose = () => {
+ setStep('profile');
+ setSelectedProfileId(null);
+ setSelectedPosition(null);
+ setMessage('');
+ onClose();
+ };
+
+ const handleSubmit = () => {
+ if (!postId || !selectedProfileId || !selectedPosition) return;
+ if (message.length > 250) return;
+
+ const applicationData = {
+ recruitingPostId: postId,
+ profileId: selectedProfileId,
+ position: selectedPosition,
+ content: message,
+ };
+
+ applicateProject(applicationData, {
+ onSuccess: () => {
+ handleClose();
+ openModal('applyComplete', { postId });
+ },
+ onError: (error) => {
+ console.error('Failed to apply for project:', error);
+ },
+ });
+ };
+
+ return (
+
+ {/* ํ๋กํ ์ ํ */}
+ {step === 'profile' && (
+
+
+
+
+ ํด๋น ํ๋กํ๋ก ์ง์ํ ๊น์?
+ *
+
+
+ ๋ง์ดํ์ด์ง์์ ์ค์ ํ ํ๋กํ ์ค ํ ๊ฐ์ง๋ฅผ ์ ํํด์ฃผ์ธ์
+
+
+
+
+
+
+
+
+
+
+ )}
+
+ {/* ์์ธ ์ ๋ณด ์
๋ ฅ */}
+ {step === 'detail' && (
+
+
+
+
+
+
+
+
+ )}
+
+ );
+};
+
+export default ApplyModalMo;
diff --git a/src/components/modal/apply/MessageTextarea.tsx b/src/components/modal/apply/MessageTextarea.tsx
new file mode 100644
index 00000000..875f8443
--- /dev/null
+++ b/src/components/modal/apply/MessageTextarea.tsx
@@ -0,0 +1,59 @@
+import React, { ChangeEvent, useState } from 'react';
+import clsx from 'clsx';
+
+interface ApplyMessageFieldProps {
+ value: string;
+ onChange: (value: string) => void;
+ title?: string | React.ReactNode;
+ placeholder?: string;
+ maxLength?: number;
+ readOnly?: boolean;
+ className?: string;
+}
+
+const MessageTextarea = ({
+ value,
+ onChange,
+ title = '๊ธ ์์ฑ์์๊ฒ ํ๊ณ ์ถ์ ๋ง์ ์ ์ด์ฃผ์ธ์',
+ placeholder = '๊ธฐ์ ๋ณด๋ค ์ค์ํ ๊ฑด ํจ๊ปํ๋ ํ๋์์.\n์ด๋ค ๋ง์์ผ๋ก ์ด ํ๋ก์ ํธ์ ์ฐธ์ฌํ๊ณ ์ถ์์ง ์ ์ด์ฃผ์ธ์. (250์ ๋ด์ธ, ์ ํ์ฌํญ)',
+ maxLength = 250,
+ readOnly = false,
+ className,
+}: ApplyMessageFieldProps) => {
+ const [isFocused, setIsFocused] = useState(false);
+
+ const handleChange = (e: ChangeEvent) => {
+ onChange(e.target.value);
+ };
+
+ const isOverLimit = value.length > maxLength;
+ const isActive = readOnly ? true : isFocused;
+
+ return (
+
+ {title}
+
+
+ );
+};
+
+export default MessageTextarea;
diff --git a/src/components/modal/apply/PartDropdown.tsx b/src/components/modal/apply/PartDropdown.tsx
new file mode 100644
index 00000000..4cc52d2f
--- /dev/null
+++ b/src/components/modal/apply/PartDropdown.tsx
@@ -0,0 +1,31 @@
+import React from 'react';
+import DropdownSelect from './profile/Dropdown';
+import { PositionType } from '@/utils/position';
+import { POSITION_KR } from '@/constants/Translate';
+
+interface ProfileCardProps {
+ positions: PositionType[];
+ onPositionSelect?: (position: PositionType | null) => void;
+}
+
+const PartDropdown = ({ positions, onPositionSelect }: ProfileCardProps) => {
+ const positionOptions = positions.map((position) => ({
+ value: position,
+ label: POSITION_KR[position],
+ }));
+
+ const handleSelect = (selectedValue: string | null) => {
+ onPositionSelect?.(selectedValue ? (selectedValue as PositionType) : null);
+ };
+ return (
+
+
+ ์ด๋ค ๋ถ์ผ๋ฅผ ์ง์ํ์๋์?
+ *
+
+
+
+ );
+};
+
+export default PartDropdown;
diff --git a/src/components/modal/apply/PartnerModal.tsx b/src/components/modal/apply/PartnerModal.tsx
new file mode 100644
index 00000000..065092cb
--- /dev/null
+++ b/src/components/modal/apply/PartnerModal.tsx
@@ -0,0 +1,140 @@
+import Button from '@/components/common/button/Button';
+import BaseModal from '..';
+import MessageTextarea from './MessageTextarea';
+import Profile from './profile/Profile';
+import Image from 'next/image';
+import { PartnerModalProps } from '@/constants/ModalList';
+// import { useModal } from '@/contexts/ModalContext';
+import { useGetApplicantProfile } from '@/hooks/queries/useApplicate';
+import { useUpdateApplicant } from '@/hooks/mutation/useUpdateApplicant';
+import { useQueryClient } from '@tanstack/react-query';
+import { ApplicationStatus } from '@/utils/applicate';
+
+const PartnerModal = ({
+ isOpen,
+ onClose,
+ applicationId,
+ recruitingPostId,
+ profilePosition,
+}: PartnerModalProps) => {
+ const { mutate: updateStatus } = useUpdateApplicant();
+ // const { openModal } = useModal();
+ const queryClient = useQueryClient();
+
+ const { data: profileData } = useGetApplicantProfile({
+ recruitingPostId: recruitingPostId,
+ applicationId: applicationId,
+ });
+
+ if (!profileData) return null;
+
+ const handleFailClick = () => {
+ updateStatus(
+ {
+ recruitingPostId: recruitingPostId,
+ applicationId: applicationId,
+ applicationStatus: ApplicationStatus.MATCHING,
+ },
+ {
+ onSuccess: () => {
+ // ์ฆ์ ์ฟผ๋ฆฌ ๋ฌดํจํ ๋ฐ ๋ฆฌํจ์น
+ queryClient.invalidateQueries({
+ queryKey: ['projectApplicants', recruitingPostId],
+ });
+ onClose();
+ },
+ onError: (error) => {
+ console.error('Failed to update application status:', error);
+ },
+ },
+ );
+ };
+
+ const handleSuccessClick = () => {
+ updateStatus(
+ {
+ recruitingPostId: recruitingPostId,
+ applicationId: applicationId,
+ applicationStatus: ApplicationStatus.TEMP_SAVED,
+ },
+ {
+ onSuccess: () => {
+ // ์ฆ์ ์ฟผ๋ฆฌ ๋ฌดํจํ ๋ฐ ๋ฆฌํจ์น
+ queryClient.invalidateQueries({
+ queryKey: ['projectApplicants', recruitingPostId],
+ });
+ onClose();
+ // ๋
ผ์์ค: ํ์ ๋ชจ์ง์ด ์๋ฃ๋์์ด์ ๋ชจ๋ฌ
+ // openModal('applicantFinish', {});
+ },
+ onError: (error) => {
+ console.error('Failed to update application status:', error);
+ },
+ },
+ );
+ };
+
+ return (
+
+
+
+
+
ํจ๊ปํ ์ฌ๋์ด ์๊ฒผ์ด์!
+
+ ํ ๋ฒ ์ ํํ ๊ฒฐ์ ์ ๋๋๋ฆด ์ ์์ผ๋
+
+ ์ ์คํ๊ฒ ์ ํํด์ฃผ์ธ์
+
+
+
+
+
+
+
{}}
+ readOnly
+ className="desktop:flex hidden"
+ />
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default PartnerModal;
diff --git a/src/components/modal/apply/profile/ContactInfo.tsx b/src/components/modal/apply/profile/ContactInfo.tsx
new file mode 100644
index 00000000..329cb0c5
--- /dev/null
+++ b/src/components/modal/apply/profile/ContactInfo.tsx
@@ -0,0 +1,47 @@
+// ์ฐ๋ฝ์๋จ ์ปดํฌ๋ํธ
+export const ContactInfo = ({
+ contactWay,
+ isUrl,
+ textSize = 'body-8',
+}: {
+ contactWay: string;
+ isUrl: boolean;
+ textSize?: string;
+}) => (
+
+
์ฐ๋ฝ์๋จ
+
+ {isUrl ? (
+
+ {contactWay}
+
+ ) : (
+
+ {contactWay || '์ฐ๋ฝ์๋จ์ ๋ฑ๋กํ์ง ์์์ด์'}
+
+ )}
+
+);
+
+// ์์
์๊ฐ ์ปดํฌ๋ํธ
+export const WorkingTimeInfo = ({
+ workingTime,
+ textSize = 'body-8',
+}: {
+ workingTime?: string;
+ textSize?: string;
+}) => (
+
+
์์
์๊ฐ
+
+
+ {workingTime ? `${workingTime}์ ์์
ํ๋ ๊ฒ ํธํด์` : '์์
์๊ฐ๋๋ฅผ ์ ํํด์ฃผ์ธ์'}
+
+
+);
diff --git a/src/components/modal/apply/profile/Dropdown.tsx b/src/components/modal/apply/profile/Dropdown.tsx
new file mode 100644
index 00000000..cc34c42f
--- /dev/null
+++ b/src/components/modal/apply/profile/Dropdown.tsx
@@ -0,0 +1,84 @@
+'use client';
+
+import { useState, useRef, useEffect } from 'react';
+import Image from 'next/image';
+
+interface DropdownSelectProps {
+ options: { label: string; value: string }[];
+ defaultLabel?: string;
+ onSelect?: (value: string) => void;
+}
+
+const DropdownSelect = ({
+ options,
+ defaultLabel = '์ง์๋ถ์ผ๋ฅผ ์ ํํด์ฃผ์ธ์',
+ onSelect,
+}: DropdownSelectProps) => {
+ const [isOpen, setIsOpen] = useState(false);
+ const [selected, setSelected] = useState<{ label: string; value: string } | null>(null);
+ const dropdownRef = useRef(null);
+
+ useEffect(() => {
+ const handleClickOutside = (e: MouseEvent) => {
+ if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) {
+ setIsOpen(false);
+ }
+ };
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => document.removeEventListener('mousedown', handleClickOutside);
+ }, []);
+
+ const handleSelect = (option: { label: string; value: string }) => {
+ setSelected(option);
+ setIsOpen(false);
+ onSelect?.(option.value);
+ };
+
+ return (
+
+
+
+ {isOpen && (
+
+ {options.map((option, idx) => {
+ const isLast = idx === options.length - 1;
+
+ return (
+ - handleSelect(option)}
+ className={[
+ 'cursor-pointer px-7 py-3',
+ 'hover:bg-primary-50 desktop:body-6 body-9',
+ isLast ? 'rounded-b-md' : '',
+ ].join(' ')}
+ >
+ {option.label}
+
+ );
+ })}
+
+ )}
+
+ );
+};
+
+export default DropdownSelect;
diff --git a/src/components/modal/apply/profile/MoProfileSlider.tsx b/src/components/modal/apply/profile/MoProfileSlider.tsx
new file mode 100644
index 00000000..4b081170
--- /dev/null
+++ b/src/components/modal/apply/profile/MoProfileSlider.tsx
@@ -0,0 +1,123 @@
+'use client';
+
+import { useState, useEffect, useRef } from 'react';
+import ProfileCard from './ProfileCard';
+import { useGetProfileList } from '@/hooks/queries/useProfile';
+import Link from 'next/link';
+
+interface Props {
+ onProfileSelect: (profileId: number) => void;
+ initialProfileId?: number | null;
+}
+
+const MoProfileSlider = ({ onProfileSelect, initialProfileId }: Props) => {
+ const { data: allProfiles } = useGetProfileList();
+ const touchStartX = useRef(0);
+ const touchEndX = useRef(0);
+
+ // ํค์๋ ์กด์ฌ ํ๋กํ๋ง ํํฐ๋ง
+ const profiles = allProfiles?.filter(
+ (profile) => profile.headKeywords && profile.headKeywords.length > 0,
+ );
+
+ // ์ด๊ธฐ ์ธ๋ฑ์ค ๊ณ์ฐ
+ const getInitialIndex = () => {
+ if (!initialProfileId || !profiles) return 0;
+ const foundIndex = profiles.findIndex((p) => p.profileId === initialProfileId);
+ return foundIndex >= 0 ? foundIndex : 0;
+ };
+
+ const [index, setIndex] = useState(getInitialIndex());
+
+ useEffect(() => {
+ if (profiles && initialProfileId) {
+ const foundIndex = profiles.findIndex((p) => p.profileId === initialProfileId);
+ if (foundIndex >= 0) {
+ setIndex(foundIndex);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [profiles?.length, initialProfileId]);
+
+ const currentProfile = profiles?.[index];
+
+ // ์ธ๋ฑ์ค ๋ณ๊ฒฝ ์ ํด๋น ํ๋กํ ์๋ ์ ํ
+ useEffect(() => {
+ if (currentProfile?.profileId !== undefined) {
+ onProfileSelect(currentProfile.profileId);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [index, currentProfile?.profileId]);
+
+ if (!profiles || profiles.length === 0) {
+ return (
+
+
+ ๋ํํค์๋๊ฐ ์ค์ ๋ ํ๋กํ์ด ์์ต๋๋ค.
+
+ ํํผ์
๋ก
+
+ ์์ ๋ํํค์๋๋ฅผ ์ค์ ํด์ฃผ์ธ์.
+
+
+ );
+ }
+
+ const handleTouchStart = (e: React.TouchEvent) => {
+ touchStartX.current = e.touches[0].clientX;
+ };
+
+ const handleTouchMove = (e: React.TouchEvent) => {
+ touchEndX.current = e.touches[0].clientX;
+ };
+
+ const handleTouchEnd = () => {
+ const swipeThreshold = 50; // ์ต์ ์ค์์ดํ ๊ฑฐ๋ฆฌ
+ const diff = touchStartX.current - touchEndX.current;
+
+ if (Math.abs(diff) > swipeThreshold) {
+ if (diff > 0 && index < profiles.length - 1) {
+ // ์ผ์ชฝ์ผ๋ก ์ค์์ดํ (๋ค์)
+ setIndex(index + 1);
+ } else if (diff < 0 && index > 0) {
+ // ์ค๋ฅธ์ชฝ์ผ๋ก ์ค์์ดํ (์ด์ )
+ setIndex(index - 1);
+ }
+ }
+
+ touchStartX.current = 0;
+ touchEndX.current = 0;
+ };
+
+ return (
+ <>
+
+
+ {profiles.map((_, i) => (
+
+ >
+ );
+};
+
+export default MoProfileSlider;
diff --git a/src/components/modal/apply/profile/Profile.tsx b/src/components/modal/apply/profile/Profile.tsx
new file mode 100644
index 00000000..5422edd4
--- /dev/null
+++ b/src/components/modal/apply/profile/Profile.tsx
@@ -0,0 +1,117 @@
+import ProfileLinkButton from '@/app/(main)/mypage/_components/profile/ProfileLinkButton';
+import Button from '@/components/common/button/Button';
+import ProfileTag from '@/components/profile/ProfileTag';
+import { ApplicationResponse } from '@/types/application';
+import Image from 'next/image';
+
+interface ProfileCardProps {
+ profile: ApplicationResponse;
+ profilePosition?: string;
+}
+
+const Profile = ({ profile, profilePosition }: ProfileCardProps) => {
+ return (
+ <>
+ {/* desktop */}
+
+
+
+
+
+
{profile.profile.userName}
+ {profilePosition && (
+
+ {profilePosition}
+
+ )}
+
+
+
+
์ฐ๋ฝ์๋จ
+
+
ํ์์ด ๋๋ฉด ๊ณต๊ฐํด์
+
+
+
์์
์๊ฐ
+
+
+ {profile.profile.workingTime
+ ? `${profile.profile.workingTime}์ ์์
ํ๋ ๊ฒ ํธํด์`
+ : '์์
์๊ฐ์ ๋ฑ๋กํ์ง ์์์ด์'}
+
+
+
+
+
+ {profile.profile.headKeywords.map((keyword) => (
+
{keyword}
+ ))}
+
+
+
+
+
+ {profile.profile.links &&
+ profile.profile.links.length > 0 &&
+ profile.profile.links.map((link, index) => (
+
+ ))}
+
+
+
+
+ {/* mobile */}
+
+
+
+
+
{profile.profile.userName}
+
+ {profile.profile.headKeywords.map((keyword) => (
+
{keyword}
+ ))}
+
+
+
+
+
์ฐ๋ฝ์๋จ
+
+
ํ์์ด ๋๋ฉด ๊ณต๊ฐํด์
+
+
+
์์
์๊ฐ
+
+
+ {profile.profile.workingTime
+ ? `${profile.profile.workingTime}์ ์์
ํ๋ ๊ฒ ํธํด์`
+ : '์์
์๊ฐ์ ๋ฑ๋กํ์ง ์์์ด์'}
+
+
+
+
+
+ {profile.profile.links &&
+ profile.profile.links.length > 0 &&
+ profile.profile.links.map((link, index) => (
+
+ ))}
+
+
+ >
+ );
+};
+
+export default Profile;
diff --git a/src/components/modal/apply/profile/ProfileCard.tsx b/src/components/modal/apply/profile/ProfileCard.tsx
new file mode 100644
index 00000000..7207dfc6
--- /dev/null
+++ b/src/components/modal/apply/profile/ProfileCard.tsx
@@ -0,0 +1,92 @@
+import React from 'react';
+import Image from 'next/image';
+import { ResponseProfile } from '@/types/profile';
+import ProfileTag from '@/components/profile/ProfileTag';
+import ProfileLinkButton from '@/components/profile/ProfileLinkButton';
+import { ContactInfo, WorkingTimeInfo } from './ContactInfo';
+
+interface ProfileCardProps {
+ profile: ResponseProfile;
+ isSelected?: boolean;
+}
+
+const ProfileCard = ({ profile, isSelected }: ProfileCardProps) => {
+ const isUrl = /^(https?:\/\/|www\.)/i.test(profile.contactWay);
+
+ return (
+ <>
+ {/* desktop */}
+ 0 ? 'justify-between' : 'flex-1 justify-start'}`}
+ tabIndex={0}
+ role="button"
+ aria-pressed={isSelected}
+ >
+
+
+
+
+
{profile.userName}
+
+
+
+
+
+
+ {/* ํ๊ทธ */}
+
+ {profile.headKeywords?.map((keyword) => (
+
{keyword}
+ ))}
+
+
+
+ {/* ์ฐ๋ฝ ์๋จ ์์ด์ฝ */}
+
+ {profile.links &&
+ profile.links.length > 0 &&
+ profile.links.map((link, index) =>
)}
+
+
+ {/* mobile */}
+ 0 ? 'justify-between' : 'flex-1 justify-start'}`}
+ tabIndex={0}
+ role="button"
+ aria-pressed={isSelected}
+ >
+
+
+
+ {profile.headKeywords?.map((keyword) => (
+
{keyword}
+ ))}
+
+
+
+
+ >
+ );
+};
+
+export default ProfileCard;
diff --git a/src/components/modal/apply/profile/ProfileSlider.tsx b/src/components/modal/apply/profile/ProfileSlider.tsx
new file mode 100644
index 00000000..5938a351
--- /dev/null
+++ b/src/components/modal/apply/profile/ProfileSlider.tsx
@@ -0,0 +1,131 @@
+'use client';
+
+import { useState, useEffect } from 'react';
+import Image from 'next/image';
+import ProfileCard from './ProfileCard';
+import { useGetProfileList } from '@/hooks/queries/useProfile';
+import Link from 'next/link';
+
+interface Props {
+ onProfileSelect: (profileId: number) => void;
+}
+
+const ProfileSlider = ({ onProfileSelect }: Props) => {
+ const [index, setIndex] = useState(0);
+ const [selectedIndex, setSelectedIndex] = useState(0);
+ const { data: allProfiles } = useGetProfileList();
+
+ // ํค์๋ ์กด์ฌ ํ๋กํ๋ง ํํฐ๋ง
+ const profiles = allProfiles?.filter(
+ (profile) => profile.headKeywords && profile.headKeywords.length > 0,
+ );
+
+ const currentProfile = profiles?.[index];
+
+ // ์ด๊ธฐ ๋ ๋๋ง ์ ์ฒซ ๋ฒ์งธ ํ๋กํ ์๋ ์ ํ
+ useEffect(() => {
+ if (profiles && profiles.length > 0 && profiles[0].profileId !== undefined) {
+ onProfileSelect(profiles[0].profileId);
+ }
+ }, [profiles, onProfileSelect]);
+
+ if (!profiles || profiles.length === 0) {
+ return (
+
+
+ ๋ํํค์๋๊ฐ ์ค์ ๋ ํ๋กํ์ด ์์ต๋๋ค.
+
+ ํํผ์
๋ก
+
+ ์์ ๋ํํค์๋๋ฅผ ์ค์ ํด์ฃผ์ธ์.
+
+
+ );
+ }
+
+ const handlePrev = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ if (index > 0) setIndex(index - 1);
+ };
+
+ const handleNext = (e: React.MouseEvent) => {
+ e.stopPropagation();
+ if (index < profiles.length - 1) setIndex(index + 1);
+ };
+
+ // ์นด๋ ํด๋ฆญ ์์๋ง ์ ํ ์ฒ๋ฆฌ
+ const handleCardClick = () => {
+ setSelectedIndex(index);
+ if (currentProfile?.profileId !== undefined) {
+ onProfileSelect(currentProfile.profileId);
+ }
+ };
+
+ const isSelected = selectedIndex === index;
+
+ return (
+ <>
+
+ {index > 0 ? (
+
+ ) : (
+
+ )}
+ {currentProfile &&
}
+ {index < profiles.length - 1 ? (
+
+ ) : (
+
+ )}
+
+
+ {profiles.map((_, i) => (
+
+ >
+ );
+};
+
+export default ProfileSlider;
diff --git a/src/components/modal/index.tsx b/src/components/modal/index.tsx
new file mode 100644
index 00000000..35f945bb
--- /dev/null
+++ b/src/components/modal/index.tsx
@@ -0,0 +1,43 @@
+'use client';
+
+import { ReactNode, MouseEvent } from 'react';
+import { motion, AnimatePresence } from 'framer-motion';
+
+interface BaseModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ children: ReactNode;
+ paddingClass?: string;
+}
+
+const BaseModal = ({ isOpen, onClose, children, paddingClass }: BaseModalProps) => {
+ const handleOverlayClick = (e: MouseEvent) => {
+ if (e.target === e.currentTarget) onClose();
+ };
+
+ return (
+
+ {isOpen && (
+
+
+ {children}
+
+
+ )}
+
+ );
+};
+
+export default BaseModal;
diff --git a/src/components/modal/profile/DeleteProfileCompleteModal.tsx b/src/components/modal/profile/DeleteProfileCompleteModal.tsx
new file mode 100644
index 00000000..09dfa8be
--- /dev/null
+++ b/src/components/modal/profile/DeleteProfileCompleteModal.tsx
@@ -0,0 +1,46 @@
+import { DeleteProfileCompleteModalProps } from '@/constants/ModalList';
+import Button from '../../common/button/Button';
+import BaseModal from '../index';
+import Image from 'next/image';
+import { useRouter } from 'next/navigation';
+
+const DeleteProfileCompleteModal = ({
+ isOpen,
+ onClose,
+ profileName,
+}: DeleteProfileCompleteModalProps) => {
+ const router = useRouter();
+ const displayName = profileName ? profileName.trim() || '์ ํ๋กํ' : '์ ํ๋กํ';
+
+ const handleHomeClick = () => {
+ onClose();
+ router.push('/mypage/profile');
+ };
+
+ return (
+
+
+
+
+ {displayName}(ํ๋กํ ๋ช
) ์ญ์ ๊ฐ ์๋ฃ๋์ด์
+
+
+
+
+
+
+ );
+};
+
+export default DeleteProfileCompleteModal;
diff --git a/src/components/modal/profile/DeleteProfileError.tsx b/src/components/modal/profile/DeleteProfileError.tsx
new file mode 100644
index 00000000..988fb134
--- /dev/null
+++ b/src/components/modal/profile/DeleteProfileError.tsx
@@ -0,0 +1,23 @@
+import Image from 'next/image';
+import BaseModal from '..';
+import { DeleteProfileErrorModalProps } from '@/constants/ModalList';
+
+const DeleteProfileError = ({ isOpen, onClose }: DeleteProfileErrorModalProps) => {
+ return (
+
+
+
+
ํด๋น ํ๋กํ๋ก ์ง์์ค์ธ ๊ณต๊ณ ๊ฐ ์์ด์
+
๋ชจ์ง ๊ธฐํ๋์์ ํ๋กํ ์ญ์ ๊ฐ ์ ํ๋ผ์.
+
+
+ );
+};
+
+export default DeleteProfileError;
diff --git a/src/components/modal/profile/DeleteProfileModal.tsx b/src/components/modal/profile/DeleteProfileModal.tsx
new file mode 100644
index 00000000..1bdb58d2
--- /dev/null
+++ b/src/components/modal/profile/DeleteProfileModal.tsx
@@ -0,0 +1,109 @@
+'use client';
+
+import Image from 'next/image';
+import { useDeleteProfile } from '@/hooks/mutation/useDeleteProfile';
+import { DeleteProfileModalProps } from '@/constants/ModalList';
+import { useModal } from '@/contexts/ModalContext';
+import BaseModal from '..';
+import Button from '@/components/common/button/Button';
+import { useToast } from '@/contexts/ToastContext';
+
+const DeleteProfileModal = ({
+ isOpen,
+ onClose,
+ profileId,
+ profileName,
+}: DeleteProfileModalProps) => {
+ const { mutate: deleteProfile } = useDeleteProfile();
+ const { openModal } = useModal();
+ const { addToast } = useToast();
+
+ const handleDelete = () => {
+ deleteProfile(profileId, {
+ onSuccess: () => {
+ onClose();
+ openModal('profileDeleteComplete', {
+ profileName: profileName,
+ });
+ },
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
+ onError: (error: any) => {
+ const code = error.response?.data?.code;
+ onClose();
+
+ switch (code) {
+ case 'PROFILE4004':
+ addToast({
+ type: 'error',
+ title: 'ํด๋น ํ๋กํ๋ก ์ง์ ์ค์ธ ๊ณต๊ณ ๊ฐ ์์ด ์ญ์ ํ ์ ์์ด์',
+ message: '๋ชจ์ง ๊ธฐํ๋์์ ํ๋กํ ์ญ์ ๊ฐ ์ ํ๋ผ์',
+ });
+ break;
+
+ case 'PROFILE4005':
+ addToast({
+ type: 'error',
+ title: 'ํด๋น ํ๋กํ๋ก ๋ชจ์ง ์ค์ธ ์์ฑ๊ธ์ด ์์ด ์ญ์ ํ ์ ์์ด์',
+ message: '๋ชจ์ง ๊ธฐํ๋์์ ํ๋กํ ์ญ์ ๊ฐ ์ ํ๋ผ์',
+ });
+ break;
+
+ case 'PROFILE4007':
+ addToast({
+ type: 'error',
+ title: 'ํด๋น ํ๋กํ๋ก ์ง์์ค์ธ ๊ณต๊ณ ๋ ๋ชจ์ง ์ค์ธ ์์ฑ๊ธ์ด ์์ด ์ญ์ ํ ์ ์์ด์',
+ message: '๋ชจ์ง ๊ธฐํ๋์์ ํ๋กํ ์ญ์ ๊ฐ ์ ํ๋ผ์',
+ });
+ break;
+
+ default:
+ addToast({
+ type: 'error',
+ title: 'ํ๋กํ ์ญ์ ์คํจ',
+ message: '์ญ์ ์ค ์ ์ ์๋ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ต๋๋ค.',
+ });
+ }
+ },
+ });
+ };
+
+ return (
+
+
+
+
์ ๋ง ์ญ์ ํ์๊ฒ ์ด์?
+
+ ํ ๋ฒ ์ญ์ ํ ํ๋กํ์ ๋ณต๊ตฌํ ์ ์์ด์
+
+
+
+
+
+
+
+
+ );
+};
+
+export default DeleteProfileModal;
diff --git a/src/components/modal/profile/EditProfileCompleteModal.tsx b/src/components/modal/profile/EditProfileCompleteModal.tsx
new file mode 100644
index 00000000..ba1e1fc3
--- /dev/null
+++ b/src/components/modal/profile/EditProfileCompleteModal.tsx
@@ -0,0 +1,40 @@
+import Button from '@/components/common/button/Button';
+import BaseModal from '..';
+import Image from 'next/image';
+import { EditProfileCompleteModalProps } from '@/constants/ModalList';
+import { useRouter } from 'next/navigation';
+
+const EditProfileCompleteModal = ({ isOpen, onClose }: EditProfileCompleteModalProps) => {
+ const router = useRouter();
+ const handleClick = () => {
+ onClose();
+ router.push('/mypage/profile');
+ };
+
+ return (
+
+
+
+
+ ํ๋กํ ์์ ์ด ์๋ฃ๋์ด์
+
+
+
+
+
+
+ );
+};
+
+export default EditProfileCompleteModal;
diff --git a/src/components/modal/profile/ProfileDetailModal.tsx b/src/components/modal/profile/ProfileDetailModal.tsx
new file mode 100644
index 00000000..ec698fa7
--- /dev/null
+++ b/src/components/modal/profile/ProfileDetailModal.tsx
@@ -0,0 +1,78 @@
+'use client';
+
+import { ProfileDetailModalProps } from '@/constants/ModalList';
+
+import Image from 'next/image';
+import BaseModal from '..';
+import ProfileTag from '@/components/profile/ProfileTag';
+import ProfileLinkButton from '@/components/profile/ProfileLinkButton';
+import { cn } from '@/utils/cn';
+
+const ProfileDetailModal = ({ isOpen, onClose, profile }: ProfileDetailModalProps) => {
+ return (
+
+
+
+
+
+
+ {profile.headKeywords.map((keyword, index) => (
+
{keyword}
+ ))}
+
+
+
0 && 'gap-[21px]',
+ )}
+ >
+
+
+
์ฐ๋ฝ์๋จ
+
{profile.contactWay || '์ฐ๋ฝ์๋จ์ ๋ฑ๋กํ์ง ์์์ด์'}
+
+
+
์์
์๊ฐ
+
+ {profile.workingTime
+ ? `${profile.workingTime}์ ์์
ํ๋ ๊ฒ ํธํด์`
+ : '์์
์๊ฐ๋๋ฅผ ๋ฑ๋กํ์ง ์์์ด์'}
+
+
+
+
+ {profile.links &&
+ profile.links.length > 0 &&
+ profile.links.map((link, index) =>
)}
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ProfileDetailModal;
diff --git a/src/components/profile/Profile1.tsx b/src/components/profile/Profile1.tsx
new file mode 100644
index 00000000..0ed825f1
--- /dev/null
+++ b/src/components/profile/Profile1.tsx
@@ -0,0 +1,112 @@
+import React from 'react';
+import ProfileTag from './ProfileTag';
+import ProfileLinkButton from './ProfileLinkButton';
+import Image from 'next/image';
+import { useGetProfile } from '@/hooks/queries/useProfile';
+import { useModal } from '@/contexts/ModalContext';
+import { useRouter } from 'next/navigation';
+
+const Profile1 = ({ profileId }: { profileId: number }) => {
+ const { openModal } = useModal();
+ const { data } = useGetProfile({ profileId });
+ const router = useRouter();
+ if (!data) return null;
+
+ const handleOpenDetailModal = () => {
+ openModal('profileDetail', { profile: data });
+ };
+
+ return (
+ <>
+ {/* PC */}
+
+
+ {/* ํ๋กํ ์ฌ์ง */}
+
+
+ {/* ์ด๋ฆ ๋ฐ ์ ๋ณด */}
+
+
{data.userName}
+
+
์ฐ๋ฝ์๋จ
+
+
+ {data.contactWay || '์ฐ๋ฝ์๋จ์ ๋ฑ๋กํ์ง ์์์ด์'}
+
+
+
+
์์
์๊ฐ
+
+
+ {data.workingTime
+ ? `${data.workingTime}์ ์์
ํ๋ ๊ฒ ํธํด์`
+ : '์์
์๊ฐ๋๋ฅผ ๋ฑ๋กํ์ง ์์์ด์'}
+
+
+
+ {/* ํ๊ทธ */}
+
+ {data.headKeywords.map((keyword, index) => (
+
{keyword}
+ ))}
+
+
+
+
+ {/* ์ฐ๋ฝ ์๋จ ์์ด์ฝ*/}
+
+ {data.links &&
+ data.links.length > 0 &&
+ data.links.map((link, index) =>
)}
+
+
+
+
+ {/* mobile */}
+
+
+
+
+ {data.userName}๋
+
+
+ {data.headKeywords.map((keyword, index) => (
+
{keyword}
+ ))}
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default Profile1;
diff --git a/src/components/profile/ProfileLinkButton.tsx b/src/components/profile/ProfileLinkButton.tsx
new file mode 100644
index 00000000..99dfbb24
--- /dev/null
+++ b/src/components/profile/ProfileLinkButton.tsx
@@ -0,0 +1,44 @@
+'use client';
+import { getLinkType } from '@/utils/project/linkType';
+import Image from 'next/image';
+import React from 'react';
+
+const ProfileLinkButton = ({ link }: { link: string }) => {
+ const type = getLinkType(link);
+
+ const isValidUrl = (url: string) => {
+ try {
+ const parsed = new URL(url);
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
+ } catch {
+ return false;
+ }
+ };
+
+ if (!isValidUrl(link)) {
+ return null;
+ }
+
+ return (
+
+ );
+};
+
+export default ProfileLinkButton;
diff --git a/src/components/profile/ProfileTag.tsx b/src/components/profile/ProfileTag.tsx
new file mode 100644
index 00000000..da46e918
--- /dev/null
+++ b/src/components/profile/ProfileTag.tsx
@@ -0,0 +1,41 @@
+import { useIsMobile } from '@/hooks/useIsMobile';
+import { useTruncatedTooltip } from '@/hooks/useTruncatedTooltip';
+import React from 'react';
+
+type TagProps = {
+ children: string;
+ maxLength?: number;
+};
+
+const ProfileTag = ({ children, maxLength }: TagProps) => {
+ const isMobile = useIsMobile();
+ const {
+ ref: tagRef,
+ showTooltip,
+ displayText,
+ handleMouseEnter,
+ handleMouseLeave,
+ handleTouch,
+ } = useTruncatedTooltip({ text: children, maxLength, isMobile });
+
+ return (
+
+ }
+ className="body-9 bg-gray-0 desktop:px-4 desktop:py-2 relative cursor-default rounded-lg border border-gray-300 px-2 py-1 text-gray-600"
+ onMouseEnter={handleMouseEnter as React.MouseEventHandler}
+ onMouseLeave={handleMouseLeave as React.MouseEventHandler}
+ onTouchStart={handleTouch as React.TouchEventHandler}
+ >
+ #{displayText}
+
+ {showTooltip && (
+
+ #{children}
+
+ )}
+
+ );
+};
+
+export default ProfileTag;
diff --git a/src/components/recruit/PositionDropdown.tsx b/src/components/recruit/PositionDropdown.tsx
new file mode 100644
index 00000000..8fc10593
--- /dev/null
+++ b/src/components/recruit/PositionDropdown.tsx
@@ -0,0 +1,96 @@
+'use client';
+
+import Image from 'next/image';
+import { useEffect, useRef, useState } from 'react';
+
+type Option = { label: string; value: string };
+
+type InlineDropdownProps = {
+ value?: string;
+ onSelect?: (opt: Option) => void;
+ placeholder?: string;
+ options: Option[];
+ className?: string;
+};
+
+export default function PositionDropdown({
+ value = '',
+ onSelect,
+ placeholder = '๋ชจ์ง๋ถ์ผ ์ ํ',
+ options,
+ className = '',
+}: InlineDropdownProps) {
+ const [open, setOpen] = useState(false);
+ const wrapRef = useRef(null);
+
+ const selected = options.find((o) => o.value === value);
+ const isPlaceholder = !selected;
+
+ // ๋ฐ๊นฅ ํด๋ฆญ ๋ซ๊ธฐ
+ useEffect(() => {
+ const onDoc = (e: MouseEvent) => {
+ if (!wrapRef.current) return;
+ if (!wrapRef.current.contains(e.target as Node)) setOpen(false);
+ };
+ document.addEventListener('mousedown', onDoc);
+ return () => document.removeEventListener('mousedown', onDoc);
+ }, []);
+
+ const listBoxBase =
+ 'absolute left-[-1px] z-10 top-[41px] rounded-b-md border border-gray-300 bg-white whitespace-nowrap';
+
+ return (
+
+
+
+ {open && (
+
+ {options.map((opt, idx) => (
+ - {
+ onSelect?.(opt);
+ setOpen(false);
+ }}
+ >
+ {opt.label}
+
+ ))}
+
+ )}
+
+ );
+}
diff --git a/src/components/recruit/ProcessMethod.tsx b/src/components/recruit/ProcessMethod.tsx
new file mode 100644
index 00000000..1cdeac06
--- /dev/null
+++ b/src/components/recruit/ProcessMethod.tsx
@@ -0,0 +1,43 @@
+'use client';
+
+import React from 'react';
+import RadioButton from './RadioButton';
+import { Control, Controller } from 'react-hook-form';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+import { DURATION_OPTIONS } from '@/constants/Dropdown';
+
+type Props = {
+ control: Control;
+};
+
+const ProcessMethod = ({ control }: Props) => {
+ return (
+
+
์งํ ๋ฐฉ๋ฒ
+
(
+
+
+ {DURATION_OPTIONS.slice(1).map((option) => (
+
+ ))}
+
+
+ )}
+ />
+
+ );
+};
+
+export default ProcessMethod;
diff --git a/src/components/recruit/ProfileSelect.tsx b/src/components/recruit/ProfileSelect.tsx
new file mode 100644
index 00000000..f5459982
--- /dev/null
+++ b/src/components/recruit/ProfileSelect.tsx
@@ -0,0 +1,74 @@
+import { useGetProfileList } from '@/hooks/queries/useProfile';
+import { ProfileSelectType } from '@/libs/schemas/projectSchema';
+import React from 'react';
+import { Control, Controller, useFormContext } from 'react-hook-form';
+import ProfileCard from './profile/ProfileCard';
+import Link from 'next/link';
+
+type Props = {
+ control: Control;
+};
+
+const ProfileSelect = ({ control }: Props) => {
+ const { data: profiles } = useGetProfileList();
+ // ํค์๋ ์กด์ฌ ํ๋กํ๋ง ํํฐ๋ง
+ const filteredProfiles = profiles?.filter(
+ (profile) => profile.headKeywords && profile.headKeywords.length > 0,
+ );
+
+ // useFormContext๋ก setValue, watch ์ฌ์ฉ
+ const { setValue, watch } = useFormContext();
+ const profileId = watch('profileId');
+
+ React.useEffect(() => {
+ if (filteredProfiles && filteredProfiles.length > 0) {
+ const currentProfileExists =
+ profileId && filteredProfiles.some((p) => p.profileId === profileId);
+
+ if (!currentProfileExists) {
+ setValue('profileId', filteredProfiles[0].profileId);
+ }
+ }
+ }, [filteredProfiles, profileId, setValue]);
+
+ if (!filteredProfiles || filteredProfiles.length === 0) {
+ return (
+
+
+ ๋ํํค์๋๊ฐ ์ค์ ๋ ํ๋กํ์ด ์์ต๋๋ค.
+
+ ํํผ์
๋ก
+
+ ์์ ๋ํํค์๋๋ฅผ ์ค์ ํด์ฃผ์ธ์.
+
+
+ );
+ }
+
+ return (
+ (
+
+ {filteredProfiles &&
+ filteredProfiles.map((profile) => (
+
onChange(profile.profileId)}
+ />
+ ))}
+
+ )}
+ />
+ );
+};
+
+export default ProfileSelect;
diff --git a/src/components/recruit/ProjectDate.tsx b/src/components/recruit/ProjectDate.tsx
new file mode 100644
index 00000000..7801b790
--- /dev/null
+++ b/src/components/recruit/ProjectDate.tsx
@@ -0,0 +1,102 @@
+import { format, parse } from 'date-fns';
+import DatePicker from 'react-datepicker';
+import Image from 'next/image';
+import React, { useEffect, useRef, useState } from 'react';
+import { Control, Controller } from 'react-hook-form';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+import 'react-datepicker/dist/react-datepicker.css';
+import './datepicker.css';
+
+type Props = {
+ title: string;
+ name: 'startDate' | 'deadline';
+ control: Control;
+ error?: string;
+};
+
+const ProjectDate = ({ title, name, control, error }: Props) => {
+ const [open, setOpen] = useState(false);
+ const ref = useRef(null);
+
+ const parseDate = (dateString: string) => {
+ if (!dateString) return null;
+
+ try {
+ if (dateString.includes('-')) {
+ return parse(dateString, 'yyyy-MM-dd', new Date());
+ }
+ if (dateString.includes('.')) {
+ return parse(dateString, 'yyyy.MM.dd', new Date());
+ }
+ return null;
+ } catch {
+ return null;
+ }
+ };
+
+ const formatDateForDisplay = (dateString: string) => {
+ if (!dateString) return '';
+
+ if (dateString.match(/^\d{4}-\d{2}-\d{2}$/)) {
+ return dateString;
+ }
+
+ if (dateString.match(/^\d{4}\.\d{2}\.\d{2}$/)) {
+ return dateString.replace(/\./g, '-');
+ }
+
+ return dateString;
+ };
+
+ useEffect(() => {
+ const handler = (e: MouseEvent) => {
+ if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false);
+ };
+ if (open) document.addEventListener('mousedown', handler);
+ return () => document.removeEventListener('mousedown', handler);
+ }, [open]);
+
+ return (
+
+
{title}
+
(
+
+
{
+ if (!open) setOpen(true);
+ }}
+ >
+
+ {formatDateForDisplay(value) || '๋
-์-์ผ์ ์ ํํด ์ฃผ์ธ์'}
+
+
+ {open && (
+
+ {
+ if (date && !isNaN(date.getTime())) {
+ onChange(format(date, 'yyyy-MM-dd'));
+ setOpen(false);
+ }
+ }}
+ minDate={new Date()}
+ inline
+ calendarClassName="rounded-lg border border-gray-300 bg-white"
+ />
+
+ )}
+
+ {error &&
{error}}
+
+ )}
+ />
+
+ );
+};
+
+export default ProjectDate;
diff --git a/src/components/recruit/ProjectDropdown.tsx b/src/components/recruit/ProjectDropdown.tsx
new file mode 100644
index 00000000..2a9cd56a
--- /dev/null
+++ b/src/components/recruit/ProjectDropdown.tsx
@@ -0,0 +1,124 @@
+'use client';
+
+import Image from 'next/image';
+import { useEffect, useMemo, useRef, useState } from 'react';
+
+type Option = { label: string; value: string };
+
+type DropdownProps = {
+ name?: string;
+ value?: string;
+ onChange?: (v: string) => void;
+ placeholder?: string;
+ options: Option[];
+ className?: string;
+ required?: boolean;
+};
+
+const ProjectDropdown = ({
+ name,
+ value = '',
+ onChange,
+ placeholder = '์งํ๊ธฐ๊ฐ์ ์ ํํด ์ฃผ์ธ์',
+ options,
+ className = '',
+ required,
+}: DropdownProps) => {
+ const [open, setOpen] = useState(false);
+ const isPlaceholder = !value;
+ const ref = useRef(null);
+
+ const selectedLabel = useMemo(
+ () => options.find((o) => o.value === value)?.label ?? '',
+ [options, value],
+ );
+
+ // ๋ฐ๊นฅ ํด๋ฆญ ๋ซ๊ธฐ
+ useEffect(() => {
+ const onDocClick = (e: MouseEvent) => {
+ if (!ref.current) return;
+ if (!ref.current.contains(e.target as Node)) setOpen(false);
+ };
+ document.addEventListener('mousedown', onDocClick);
+ return () => document.removeEventListener('mousedown', onDocClick);
+ }, []);
+
+ return (
+
+
+
+ {open && (
+
+ {options.map((opt, idx) => {
+ const isSelected = opt.value === value;
+ const isLast = idx === options.length - 1;
+
+ return (
+ - {
+ onChange?.(opt.value);
+ setOpen(false);
+ }}
+ >
+ {opt.label}
+
+ );
+ })}
+
+ )}
+ {name && (
+
+ )}
+
+ );
+};
+export default ProjectDropdown;
diff --git a/src/components/recruit/ProjectDuration.tsx b/src/components/recruit/ProjectDuration.tsx
new file mode 100644
index 00000000..12db3ee0
--- /dev/null
+++ b/src/components/recruit/ProjectDuration.tsx
@@ -0,0 +1,35 @@
+'use client';
+
+import React from 'react';
+import ProjectDropdown from './ProjectDropdown';
+import { Control, Controller } from 'react-hook-form';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+import { PERIOD_OPTIONS } from '@/constants/Dropdown';
+
+type Props = {
+ control: Control;
+};
+
+const ProjectDuration = ({ control }: Props) => {
+ return (
+
+
ํ๋ก์ ํธ ์งํ๊ธฐ๊ฐ
+
(
+
+ )}
+ />
+
+ );
+};
+
+export default ProjectDuration;
diff --git a/src/components/recruit/RadioButton.tsx b/src/components/recruit/RadioButton.tsx
new file mode 100644
index 00000000..fe00c048
--- /dev/null
+++ b/src/components/recruit/RadioButton.tsx
@@ -0,0 +1,43 @@
+import Image from 'next/image';
+
+interface RadioButtonProps {
+ id: string;
+ name: string;
+ value: string;
+ checked: boolean;
+ label: string;
+ onChange: (value: string) => void;
+}
+
+import React from 'react';
+
+const RadioButton = ({ id, name, value, checked, label, onChange }: RadioButtonProps) => {
+ return (
+ <>
+ onChange(value)}
+ />
+
+ >
+ );
+};
+
+export default RadioButton;
diff --git a/src/components/recruit/RecruitForm.tsx b/src/components/recruit/RecruitForm.tsx
new file mode 100644
index 00000000..d42e9df5
--- /dev/null
+++ b/src/components/recruit/RecruitForm.tsx
@@ -0,0 +1,113 @@
+'use client';
+import React, { useEffect } from 'react';
+import { isAfter, parse } from 'date-fns';
+import TitleInput from './TitleInput';
+import ProcessMethod from './ProcessMethod';
+import ProjectDuration from './ProjectDuration';
+import RecruitPosition from './RecruitPosition';
+import ProjectDate from './ProjectDate';
+import TextInput from './TextInput';
+import TextContent from './editor/TextContent';
+import ProfileSlider from './profile/ProfileSlider';
+import Button from '@/components/common/Button';
+import { Project } from '@/types/project';
+import { useRecruitForm } from './useRecruitForm';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+
+type RecruitFormProps = {
+ mode?: 'create' | 'edit';
+ initialData?: Project;
+ postId?: number;
+ showProfileList?: boolean;
+ onNext?: (data: Partial) => void;
+ initialFormData?: Partial | null;
+};
+
+const RecruitForm = ({
+ mode = 'create',
+ initialData,
+ postId,
+ showProfileList = true,
+ onNext,
+ initialFormData,
+}: RecruitFormProps) => {
+ const {
+ handleSubmit,
+ control,
+ watch,
+ reset,
+ formState: { isValid, errors },
+ onSubmit,
+ onError,
+ } = useRecruitForm({
+ mode,
+ initialData,
+ postId,
+ showProfileList,
+ onNext,
+ initialFormData,
+ });
+
+ useEffect(() => {
+ if (initialFormData && mode === 'create' && !showProfileList) {
+ reset(initialFormData);
+ }
+ }, [initialFormData, reset, mode, showProfileList]);
+
+ const startDate = watch('startDate');
+ const deadline = watch('deadline');
+
+ const isEndDateInvalid = (deadline: string, startDate: string) => {
+ if (!startDate || !deadline) return true;
+
+ try {
+ const start = parse(startDate, 'yyyy-MM-dd', new Date());
+ const end = parse(deadline, 'yyyy-MM-dd', new Date());
+ return !isAfter(end, start);
+ } catch {
+ return false;
+ }
+ };
+
+ return (
+
+ );
+};
+
+export default RecruitForm;
diff --git a/src/components/recruit/RecruitPosition.tsx b/src/components/recruit/RecruitPosition.tsx
new file mode 100644
index 00000000..76059b19
--- /dev/null
+++ b/src/components/recruit/RecruitPosition.tsx
@@ -0,0 +1,185 @@
+'use client';
+
+import Image from 'next/image';
+import React, { useEffect, useRef, useState } from 'react';
+import PositionDropdown from './PositionDropdown';
+import { RECRUIT_OPTIONS } from '@/constants/Dropdown';
+import { Control, useFieldArray, useWatch } from 'react-hook-form';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+import { PositionType } from '@/utils/position';
+
+interface Props {
+ control: Control;
+}
+
+const RecruitPosition = ({ control }: Props) => {
+ const [localError, setLocalError] = useState(null);
+
+ const { fields, append, remove, update } = useFieldArray({
+ control,
+ name: 'recruitingPositions',
+ });
+
+ const watched = useWatch({
+ control,
+ name: 'recruitingPositions',
+ }) as RecruitFormType['recruitingPositions'];
+
+ const initRef = useRef(false);
+
+ const recruitOptionsWithoutAll = RECRUIT_OPTIONS.filter((opt) => opt.value !== 'ALL');
+
+ useEffect(() => {
+ if (initRef.current) return;
+ initRef.current = true;
+
+ if ((!watched || watched.length === 0) && fields.length === 0) {
+ append({ position: '' as PositionType, count: 1 });
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [append]);
+
+ const handleAdd = () => {
+ const item =
+ watched ??
+ fields.map((f) => ({
+ position: f.position as PositionType,
+ count: f.count ?? 1,
+ }));
+
+ const invalid = item.some(
+ (v) => !v || !v.position || (typeof v.count === 'number' && v.count < 1),
+ );
+
+ if (invalid) {
+ setLocalError('๋ชจ์ง๋ถ์ผ๋ฅผ ์ ํํด์ฃผ์ธ์');
+ return;
+ }
+
+ setLocalError(null);
+ append({ position: '' as PositionType, count: 1 });
+ };
+
+ const handlePositionSelect = (index: number, value: PositionType) => {
+ const item = (watched && watched[index]) ?? {
+ position: fields[index].position as PositionType,
+ count: fields[index].count ?? 1,
+ };
+ update(index, { ...item, position: value });
+
+ if (localError && index === fields.length - 1) {
+ setLocalError(null);
+ }
+ };
+
+ const handleCount = (index: number, delta: 1 | -1) => {
+ const item = (watched && watched[index]) ?? {
+ position: fields[index].position as PositionType,
+ count: fields[index].count ?? 1,
+ };
+ const next = Math.max(1, (item.count ?? 1) + delta);
+ update(index, { ...item, count: next });
+ };
+
+ const handleRemove = (index: number) => {
+ remove(index);
+ const next = (
+ watched ?? fields.map((f) => ({ position: f.position as PositionType, count: f.count ?? 1 }))
+ ).filter((_, i) => i !== index);
+ if (!next || next.length === 0) {
+ append({ position: '' as PositionType, count: 1 });
+ }
+ };
+
+ return (
+
+
๋ชจ์ง๋ถ์ผ/์ธ์
+
+ {fields.map((field, index) => {
+ const item = (watched && watched[index]) ?? {
+ position: field.position as PositionType,
+ count: field.count ?? 1,
+ };
+ const isError = !!localError && index === fields.length - 1;
+ const borderColor = isError ? 'border-red-100' : 'border-gray-300';
+
+ return (
+
+
+
+ {/* ๋ชจ์ง๋ถ์ผ ๋๋กญ๋ค์ด */}
+
handlePositionSelect(index, opt.value as PositionType)}
+ />
+ {/* ์ธ์ ์ฆ๊ฐ ๋ฐ ์ญ์ */}
+
+
+
+
+
+ {item.count}๋ช
+
+
+
+
+
+
+
+
+
+ {isError &&
{localError}}
+
+ );
+ })}
+ {/* ์ ํจํ ๋๋ง ์ ํญ๋ชฉ ์ถ๊ฐ๋จ */}
+
+
+
+ );
+};
+
+export default RecruitPosition;
diff --git a/src/components/recruit/RecruitStepPage.tsx b/src/components/recruit/RecruitStepPage.tsx
new file mode 100644
index 00000000..f0cf9bb1
--- /dev/null
+++ b/src/components/recruit/RecruitStepPage.tsx
@@ -0,0 +1,169 @@
+'use client';
+
+import React, { useState, useEffect, useRef } from 'react';
+import { isLoggedIn } from '@/utils/auth';
+import { useUserStore } from '@/store/useUserStore';
+import RecruitForm from '@/components/recruit/RecruitForm';
+import Button from '@/components/common/Button';
+import { FormProvider, useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import {
+ RecruitFormType,
+ profileSelectSchema,
+ ProfileSelectType,
+} from '@/libs/schemas/projectSchema';
+import { useCreateProject } from '@/hooks/mutation/useCreateProject';
+import { useRouter } from 'next/navigation';
+import { useModal } from '@/contexts/ModalContext';
+import { CreateProject } from '@/types/project';
+import ProfileSelect from './ProfileSelect';
+import Image from 'next/image';
+import MobileHeader from '../common/MobileHeader';
+import { useRecruitNavigationGuard } from '@/hooks/useRecruitNavigationGuard';
+import { usePreventNavigation } from '@/hooks/usePreventNavigation';
+
+type Step = 'form' | 'profile';
+
+const RecruitPage = () => {
+ const { userName, _hasHydrated } = useUserStore();
+ const router = useRouter();
+
+ const redirectedRef = useRef(false);
+ useEffect(() => {
+ if (!_hasHydrated || redirectedRef.current) return;
+ if (!isLoggedIn(userName)) {
+ redirectedRef.current = true;
+ alert('๋ก๊ทธ์ธ์ด ํ์ํฉ๋๋ค.');
+ setTimeout(() => router.push('/login'), 0);
+ }
+ }, [_hasHydrated, userName, router]);
+
+ const [step, setStep] = useState('form');
+ const [formData, setFormData] = useState | null>(null);
+ const [isSubmitting, setIsSubmitting] = useState(false);
+ const { openModal } = useModal();
+ const { mutate: createProject } = useCreateProject();
+
+ const methods = useForm({
+ mode: 'onChange',
+ resolver: zodResolver(profileSelectSchema),
+ });
+ const profileControl = methods.control;
+ const handleProfileSubmit = methods.handleSubmit;
+ const selectedProfileId = methods.watch('profileId');
+
+ useEffect(() => {
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+ }, [step]);
+
+ useRecruitNavigationGuard(!isSubmitting, setIsSubmitting);
+
+ usePreventNavigation(!isSubmitting, (navigate) => {
+ openModal('notFinish', {
+ onConfirm: () => {
+ setIsSubmitting(true);
+ navigate();
+ },
+ });
+ });
+
+ const handleNext = (data: Partial) => {
+ setFormData(data);
+ setStep('profile');
+ };
+
+ const handleBack = () => {
+ setStep('form');
+ };
+
+ const onProfileSubmit = (profileData: ProfileSelectType) => {
+ if (!formData) return;
+
+ const finalData: CreateProject = {
+ ...formData,
+ profileId: profileData.profileId,
+ status: 'OPEN',
+ } as CreateProject;
+
+ setIsSubmitting(true);
+
+ createProject(finalData, {
+ onSuccess: (response) => {
+ openModal('recruitComplete', {
+ onListClick: () => router.push('/project'),
+ onDetailClick: () => router.push(`/project/${response.postId}`),
+ });
+ },
+ onError: (error) => {
+ console.error('ํ๋ก์ ํธ ์์ฑ ์คํจ:', error);
+ setIsSubmitting(false);
+ },
+ });
+ };
+
+ return (
+ <>
+ {step === 'form' ? (
+
+ ) : (
+
+ )}
+
+
+
+
+ {step === 'form' ? 'ํ์ ๋ชจ์งํ๊ธฐ' : '์์ฑ์ ํ๋กํ ์ ํํ๊ธฐ'}
+
+
+ {step === 'form'
+ ? '๊ฒ์๊ธ์ ์์ฑํ์ฌ ์ํ๋ ๋ถ์ผ์ ํ์์ ๋ชจ์งํด๋ณด์ธ์'
+ : '๊ณต๊ฐํ ํ๋กํ์ ํ ๊ฐ์ง ์ ํํด์ฃผ์ธ์'}
+
+
+
+
+ {step === 'form' ? (
+
+ ) : (
+
+
+
+ )}
+
+ >
+ );
+};
+
+export default RecruitPage;
diff --git a/src/components/recruit/TextInput.tsx b/src/components/recruit/TextInput.tsx
new file mode 100644
index 00000000..2b430603
--- /dev/null
+++ b/src/components/recruit/TextInput.tsx
@@ -0,0 +1,34 @@
+import React from 'react';
+import { Control, Controller } from 'react-hook-form';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+
+type Props = {
+ title: string;
+ name: 'contactWay';
+ control: Control;
+ placeholder?: string;
+};
+
+const TextInput = ({ title, name, control, placeholder = 'Ex. ์นด์นด์คํก ์คํ์ฑํ
๋งํฌ' }: Props) => {
+ return (
+
+
{title}
+
(
+
+
+
+ )}
+ />
+
+ );
+};
+
+export default TextInput;
diff --git a/src/components/recruit/TitleInput.tsx b/src/components/recruit/TitleInput.tsx
new file mode 100644
index 00000000..b8a8203f
--- /dev/null
+++ b/src/components/recruit/TitleInput.tsx
@@ -0,0 +1,35 @@
+'use client';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+import React from 'react';
+import { Control, Controller } from 'react-hook-form';
+
+type Props = {
+ name: 'title';
+ control: Control;
+};
+
+const TitleInput = ({ name, control }: Props) => {
+ return (
+ (
+
+ 30 ? 'border-red-100' : 'focus:border-gray-600'}`}
+ placeholder="์ ๋ชฉ์ ์
๋ ฅํด์ฃผ์ธ์ (์ต๋30์)"
+ value={value}
+ onChange={onChange}
+ />
+ {value.length > 30 && (
+ 30 ? 'text-red-100' : ''}`}>
+ ์ ๋ชฉ์ 30์ ์ดํ๋ก ์
๋ ฅํด์ฃผ์ธ์
+
+ )}
+
+ )}
+ />
+ );
+};
+
+export default TitleInput;
diff --git a/src/components/recruit/datepicker.css b/src/components/recruit/datepicker.css
new file mode 100644
index 00000000..ab689a9e
--- /dev/null
+++ b/src/components/recruit/datepicker.css
@@ -0,0 +1,147 @@
+.react-datepicker {
+ border: 1px solid #e8e8e8;
+ border-radius: 8px;
+ background: #fff;
+ font-family: var(--font-pretendard), sans-serif;
+}
+
+.react-datepicker__header {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ border-bottom: none;
+ padding: 4px 16px 4px 16px;
+ margin: 0px 16px 8px 16px;
+ background-color: #f8f8f8;
+ border-radius: 50px !important;
+}
+
+.react-datepicker__navigation {
+ width: 16px;
+ height: 16px;
+ border: none;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ padding: 28px 28px;
+ cursor: pointer;
+}
+
+.react-datepicker__navigation:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.react-datepicker__current-month {
+ position: relative;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 21px;
+ padding: 0 40px;
+ white-space: nowrap;
+ color: #3b3b3b;
+}
+
+.react-datepicker__day-names {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ padding: 8px 24px;
+ text-align: center;
+}
+
+.react-datepicker__day-name {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: #191919;
+ font-size: 12px;
+ font-weight: 400;
+ line-height: 18px;
+}
+
+.react-datepicker__month {
+ margin-left: 0;
+ margin-right: 0;
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+
+.react-datepicker__month-container {
+ padding-bottom: 16px;
+ padding-top: 16px;
+}
+
+.react-datepicker__week {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ gap: 5px;
+ padding: 0 24px;
+ margin-bottom: 0;
+}
+
+.react-datepicker__day {
+ width: 24px;
+ height: 24px;
+ line-height: 24px;
+ margin: 0;
+ color: #191919;
+ justify-self: center;
+}
+
+.react-datepicker__day:hover {
+ background: #f2f2f2;
+ border-radius: 40px !important;
+ cursor: pointer;
+}
+
+.react-datepicker__day--today {
+ font-weight: 600;
+ background: none !important;
+}
+
+.react-datepicker__day--selected {
+ background: #11dca9 !important;
+ color: #fff !important;
+ border-radius: 40px;
+}
+.react-datepicker__day--keyboard-selected {
+ background: #f3f4f6;
+ color: #1f2937;
+ border-radius: 40px;
+}
+.react-datepicker__day--outside-month {
+ color: #c4c4c4;
+}
+
+.react-datepicker__triangle {
+ display: none;
+}
+
+/* ๊ธฐ๋ณธ ์์ด์ฝ ์จ๊น */
+.react-datepicker__navigation-icon::before {
+ content: none;
+}
+
+.react-datepicker__navigation--previous {
+ background-image: url('/icons/date-left.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 16px 16px;
+}
+.react-datepicker__navigation--next {
+ background-image: url('/icons/date-right.svg');
+ background-repeat: no-repeat;
+ background-position: center;
+ background-size: 16px 16px;
+}
+
+/* ์ ํ ๋ถ๊ฐ(๋นํ์ฑํ) ๋ ์ง ์์ ์ฐํ๊ฒ */
+.react-datepicker__day--disabled,
+.react-datepicker__month-text--disabled,
+.react-datepicker__quarter-text--disabled,
+.react-datepicker__year-text--disabled {
+ color: #d1d5db !important; /* ์ฐํ ํ์ (Tailwind gray-300) */
+ pointer-events: none;
+ opacity: 1;
+}
diff --git a/src/components/recruit/editor/TextContent.tsx b/src/components/recruit/editor/TextContent.tsx
new file mode 100644
index 00000000..49b65096
--- /dev/null
+++ b/src/components/recruit/editor/TextContent.tsx
@@ -0,0 +1,93 @@
+'use client';
+
+import React, { useEffect, useRef, useState } from 'react';
+import { useEditor, EditorContent } from '@tiptap/react';
+import { getEditorExtensions } from './editorExtensions';
+import Toolbar from './Toolbar';
+import { handleLink } from './linkUtils';
+import { Control, useController } from 'react-hook-form';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+
+type Props = {
+ control: Control;
+ name?: 'content';
+};
+
+const TextContent = ({ control, name = 'content' }: Props) => {
+ const { field } = useController({ name, control });
+ const value = field.value ?? '';
+ const onChange = field.onChange as (html: string) => void;
+
+ const onChangeRef = useRef(onChange);
+ useEffect(() => {
+ onChangeRef.current = onChange;
+ }, [onChange]);
+
+ const [, forceUpdate] = useState(0);
+
+ const editor = useEditor({
+ extensions: getEditorExtensions(),
+ content: value,
+ immediatelyRender: false,
+ editorProps: {
+ attributes: {
+ class:
+ 'ProseMirror appearance-none desktop:min-h-45 min-h-60 w-full bg-white body-6 text-gray-700 focus:outline-none',
+ },
+ },
+ onUpdate: ({ editor }) => {
+ try {
+ const html = editor.getHTML();
+ // ํญ์ html์ ์ ๋ฌ (50์ ๋ฏธ๋ง์ด์ด๋ ์ปจํ
์ธ ์ ์ง)
+ onChangeRef.current?.(html);
+ } catch {
+ // silent
+ }
+ },
+ });
+
+ useEffect(() => {
+ return () => editor?.destroy();
+ }, [editor]);
+
+ useEffect(() => {
+ if (!editor) return;
+ const current = editor.getHTML?.();
+ if (typeof value === 'string' && value !== current) {
+ editor.commands.setContent(value, { emitUpdate: false });
+ }
+ }, [value, editor]);
+
+ useEffect(() => {
+ if (!editor) return;
+ const update = () => forceUpdate((v) => v + 1);
+ editor.on('selectionUpdate', update);
+ editor.on('transaction', update);
+ editor.on('update', update);
+ return () => {
+ editor.off('selectionUpdate', update);
+ editor.off('transaction', update);
+ editor.off('update', update);
+ };
+ }, [editor]);
+
+ if (!editor) return Loading...
;
+
+ const textLength = editor.getText().replace(/\r?\n/g, '').length;
+
+ return (
+
+
+
handleLink(editor)} />
+
+
+ {textLength}
+
+
+
+ );
+};
+
+export default TextContent;
diff --git a/src/components/recruit/editor/Toolbar.tsx b/src/components/recruit/editor/Toolbar.tsx
new file mode 100644
index 00000000..20c0aa3f
--- /dev/null
+++ b/src/components/recruit/editor/Toolbar.tsx
@@ -0,0 +1,180 @@
+import { Editor } from '@tiptap/react';
+import Image from 'next/image';
+
+interface ToolbarProps {
+ editor: Editor;
+ onLinkButtonClick: () => void;
+}
+
+const Toolbar = ({ editor, onLinkButtonClick }: ToolbarProps) => {
+ if (!editor) return null;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default Toolbar;
diff --git a/src/components/recruit/editor/editorExtensions.ts b/src/components/recruit/editor/editorExtensions.ts
new file mode 100644
index 00000000..5320f621
--- /dev/null
+++ b/src/components/recruit/editor/editorExtensions.ts
@@ -0,0 +1,40 @@
+import StarterKit from '@tiptap/starter-kit';
+import Link from '@tiptap/extension-link';
+import Placeholder from '@tiptap/extension-placeholder';
+
+export const getEditorExtensions = () => [
+ StarterKit.configure({
+ link: false,
+ bulletList: { HTMLAttributes: { class: 'list-disc ml-4' } },
+ orderedList: { HTMLAttributes: { class: 'list-decimal ml-4' } },
+ }),
+ Link.configure({
+ autolink: true,
+ linkOnPaste: true,
+ openOnClick: false,
+ HTMLAttributes: {
+ target: '_blank',
+ rel: 'noopener noreferrer nofollow',
+ class: 'underline text-primary-900 hover:opacity-80',
+ },
+ validate: (href) => /^(https?:\/\/|mailto:|tel:|www\.)/i.test(href),
+ }),
+ Placeholder.configure({
+ placeholder: ({ node }) => {
+ if (node.type.name === 'paragraph') {
+ return [
+ '*์ต์ 50์ ์ด์๋ถํฐ ์์ฑ ๊ฐ๋ฅํด์',
+ 'ํ์์๊ฒ ์ฒซ์ธ์์ด ๋๋ ๋ชจ์ง๊ธ์ ๊ผผ๊ผผํ ์์ฑํ ์๋ก ์ข์์',
+ '',
+ '<์์ฑ ์์>',
+ 'โข ์ด๋ค ํ๋ก์ ํธ์ธ์ง ์๊ฐํด์ฃผ์ธ์',
+ 'โข ๋๊ตฌ์, ์, ์ด๋ค ๋ชฉํ๋ก ํจ๊ปํ๊ณ ์ถ์์ง ์์ฑํด์ฃผ์ธ์',
+ 'โข ์ด๋ค ์ฑํฅ์ ์ฌ๋๋ค์ ์ฐพ๊ณ ์๋์ง ์์ฑํด์ฃผ์ธ์',
+ ].join('\n');
+ }
+ return '';
+ },
+ includeChildren: true,
+ showOnlyCurrent: false,
+ }),
+];
diff --git a/src/components/recruit/editor/linkUtils.ts b/src/components/recruit/editor/linkUtils.ts
new file mode 100644
index 00000000..e24e6707
--- /dev/null
+++ b/src/components/recruit/editor/linkUtils.ts
@@ -0,0 +1,54 @@
+import { type Editor } from '@tiptap/react';
+
+export const handleLink = (editor: Editor) => {
+ const isLinkActive = editor.isActive('link');
+ if (isLinkActive) {
+ editor.chain().focus().unsetLink?.().run();
+ return;
+ }
+
+ const url = window.prompt('URL์ ์
๋ ฅํ์ธ์ (์: https://example.com)');
+ if (!url) return;
+
+ let normalized = url.trim();
+ if (/^www\./i.test(normalized)) normalized = 'https://' + normalized;
+
+ const valid = /^(https?:\/\/|mailto:|tel:|www\.)/i.test(normalized);
+ if (!valid) {
+ alert('์ ํจํ URL์ ์
๋ ฅํด์ฃผ์ธ์ (์: https://example.com, mailto:, tel:, www.)');
+ return;
+ }
+
+ const selectionEmpty = editor.state.selection.empty;
+ if (selectionEmpty) {
+ editor
+ .chain()
+ .focus()
+ .insertContent({
+ type: 'text',
+ marks: [
+ {
+ type: 'link',
+ attrs: {
+ href: normalized,
+ target: '_blank',
+ rel: 'noopener noreferrer nofollow',
+ },
+ },
+ ],
+ text: normalized,
+ })
+ .run();
+ } else {
+ editor
+ .chain()
+ .focus()
+ .extendMarkRange('link')
+ .setLink({
+ href: normalized,
+ target: '_blank',
+ rel: 'noopener noreferrer nofollow',
+ })
+ .run();
+ }
+};
diff --git a/src/components/recruit/profile/ProfileCard.tsx b/src/components/recruit/profile/ProfileCard.tsx
new file mode 100644
index 00000000..2ed4523f
--- /dev/null
+++ b/src/components/recruit/profile/ProfileCard.tsx
@@ -0,0 +1,137 @@
+'use client';
+
+import Image from 'next/image';
+import ProfileTag from '@/components/profile/ProfileTag';
+import ProfileLinkButton from '@/components/profile/ProfileLinkButton';
+import { ResponseProfile } from '@/types/profile';
+import { useModal } from '@/contexts/ModalContext';
+
+interface ProfileCardProps {
+ profile: ResponseProfile;
+ isSelected?: boolean;
+ onClick?: () => void;
+}
+
+const ProfileCard = ({ profile, isSelected = false, onClick }: ProfileCardProps) => {
+ const { openModal } = useModal();
+
+ const handleOpenDetailModal = () => {
+ openModal('profileDetail', { profile });
+ };
+
+ return (
+ <>
+ {/* desktop */}
+
+ {profile.profileName || '์ ํ๋กํ'}
+
+
+
+
+
+ {/* ์ด๋ฆ ๋ฐ ์ ๋ณด */}
+
+
{profile.userName}
+
+
์ฐ๋ฝ์๋จ
+
+
+ {profile.contactWay || '์ฐ๋ฝ์๋จ์ ๋ฑ๋กํ์ง ์์์ด์'}
+
+
+
+
์์
์๊ฐ
+
+
+ {profile.workingTime
+ ? `${profile.workingTime}์ ์์
ํ๋ ๊ฒ ํธํด์`
+ : '์์
์๊ฐ๋๋ฅผ ์ ํํด์ฃผ์ธ์'}
+
+
+
+
+ {/* ํ๊ทธ */}
+
+ {profile.headKeywords?.map((keyword) => (
+
{keyword}
+ ))}
+
+
+
+
+
+ {/* ์ฐ๋ฝ ์๋จ ์์ด์ฝ*/}
+
+ {profile.links &&
+ profile.links.length > 0 &&
+ profile.links.map((link, index) =>
)}
+
+
+
+
+ {/* mobile */}
+
+
+
+
{profile.profileName || '์ ํ๋กํ'}
+
+
+
+
+ {/* ํ๊ทธ */}
+
+ {profile.headKeywords?.map((keyword) => (
+
{keyword}
+ ))}
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ProfileCard;
diff --git a/src/components/recruit/profile/ProfileSlider.tsx b/src/components/recruit/profile/ProfileSlider.tsx
new file mode 100644
index 00000000..28198c11
--- /dev/null
+++ b/src/components/recruit/profile/ProfileSlider.tsx
@@ -0,0 +1,94 @@
+'use client';
+
+import { useState } from 'react';
+import Image from 'next/image';
+import ProfileCard from './ProfileCard';
+import { useGetProfileList } from '@/hooks/queries/useProfile';
+import { Control, Controller } from 'react-hook-form';
+import { RecruitFormType } from '@/libs/schemas/projectSchema';
+
+interface Props {
+ control: Control;
+ name?: 'profileId';
+}
+
+const ProfileSlider = ({ control, name = 'profileId' }: Props) => {
+ const [index, setIndex] = useState(0);
+ const { data: profiles } = useGetProfileList();
+
+ if (!profiles) return null;
+
+ const currentProfile = profiles[index];
+
+ return (
+ (
+ <>
+ ์์ฑ์ ํ๋กํ ์ ํํ๊ธฐ
+
+ {index > 0 ? (
+
+ ) : (
+
+ )}
+
+ {index < profiles.length - 1 ? (
+
+ ) : (
+
+ )}
+
+ >
+ )}
+ />
+ );
+};
+
+export default ProfileSlider;
diff --git a/src/components/recruit/useRecruitForm.ts b/src/components/recruit/useRecruitForm.ts
new file mode 100644
index 00000000..fb140b1e
--- /dev/null
+++ b/src/components/recruit/useRecruitForm.ts
@@ -0,0 +1,153 @@
+import { useForm } from 'react-hook-form';
+import { zodResolver } from '@hookform/resolvers/zod';
+import { useRouter } from 'next/navigation';
+import { useModal } from '@/contexts/ModalContext';
+import { useCreateProject } from '@/hooks/mutation/useCreateProject';
+import { useUpdateProject } from '@/hooks/mutation/useUpdateProject';
+import {
+ createRecruitFormSchema,
+ recruitFormWithoutProfileSchema,
+ RecruitFormType,
+} from '@/libs/schemas/projectSchema';
+import { CreateProject, Project } from '@/types/project';
+import { PositionType } from '@/utils/position';
+
+type UseRecruitFormProps = {
+ mode: 'create' | 'edit';
+ initialData?: Project;
+ postId?: number;
+ showProfileList?: boolean;
+ onNext?: (data: Partial) => void;
+ initialFormData?: Partial | null;
+};
+
+const convertDateFormat = (dateString: string) => {
+ if (!dateString) return '';
+ return dateString.replace(/\./g, '-');
+};
+
+export const useRecruitForm = ({
+ mode,
+ initialData,
+ postId,
+ showProfileList = true,
+ onNext,
+ initialFormData,
+}: UseRecruitFormProps) => {
+ const router = useRouter();
+ const { openModal } = useModal();
+ const { mutate: createProject } = useCreateProject();
+ const { mutate: updateProject } = useUpdateProject();
+
+ const getSchema = () => {
+ if (mode === 'edit') {
+ return recruitFormWithoutProfileSchema;
+ }
+ if (!showProfileList) {
+ return recruitFormWithoutProfileSchema;
+ }
+ return createRecruitFormSchema;
+ };
+
+ const formMethods = useForm({
+ mode: 'onChange',
+ reValidateMode: 'onChange',
+ resolver: zodResolver(getSchema()),
+ defaultValues:
+ mode === 'edit' && initialData
+ ? {
+ title: initialData.title,
+ recruitingPositions: initialData.recruitingPositions,
+ progressWay: initialData.progressWay,
+ startDate: convertDateFormat(initialData.startDate),
+ period: initialData.period,
+ deadline: convertDateFormat(initialData.deadline),
+ contactWay: initialData.contactWay,
+ content: initialData.content,
+ }
+ : initialFormData || {
+ title: '',
+ recruitingPositions: [{ position: '' as PositionType, count: 1 }],
+ progressWay: undefined,
+ startDate: '',
+ period: undefined,
+ deadline: '',
+ contactWay: '',
+ content: '',
+ profileId: undefined,
+ },
+ });
+
+ const handleEdit = (formData: RecruitFormType) => {
+ if (!postId) return;
+
+ const projectData: Project = {
+ title: formData.title,
+ recruitingPositions: formData.recruitingPositions,
+ progressWay: formData.progressWay!,
+ startDate: formData.startDate,
+ period: formData.period!,
+ deadline: formData.deadline,
+ contactWay: formData.contactWay,
+ content: formData.content,
+ status: initialData?.status || 'OPEN',
+ };
+
+ updateProject(
+ { postId, project: projectData },
+ {
+ onSuccess: () => router.push(`/project/${postId}`),
+ onError: (error) => console.error('ํ๋ก์ ํธ ์์ ์คํจ:', error),
+ },
+ );
+ };
+
+ const handleCreate = (formData: RecruitFormType) => {
+ if (!formData.profileId) {
+ console.error('ํ๋กํ์ด ์ ํ๋์ง ์์์ต๋๋ค.');
+ return;
+ }
+
+ const projectData: CreateProject = {
+ ...formData,
+ profileId: formData.profileId,
+ status: 'OPEN',
+ };
+
+ createProject(projectData, {
+ onSuccess: (response) => {
+ openModal('recruitComplete', {
+ onListClick: () => router.push('/project'),
+ onDetailClick: () => router.push(`/project/${response.postId}`),
+ });
+ },
+ onError: (error) => console.error('ํ๋ก์ ํธ ์์ฑ ์คํจ:', error),
+ });
+ };
+
+ const onSubmit = (formData: RecruitFormType) => {
+ try {
+ if (mode === 'edit') {
+ handleEdit(formData);
+ } else {
+ if (!showProfileList && onNext) {
+ onNext(formData);
+ return;
+ }
+ handleCreate(formData);
+ }
+ } catch (error) {
+ console.error('ํผ ์ ์ถ ์ค ์ค๋ฅ ๋ฐ์:', error);
+ }
+ };
+
+ const onError = (errors: unknown) => {
+ console.error('ํผ ์ ํจ์ฑ ๊ฒ์ฌ ์คํจ:', errors);
+ };
+
+ return {
+ ...formMethods,
+ onSubmit,
+ onError,
+ };
+};
diff --git a/src/constants/Dropdown.ts b/src/constants/Dropdown.ts
new file mode 100644
index 00000000..f5092817
--- /dev/null
+++ b/src/constants/Dropdown.ts
@@ -0,0 +1,82 @@
+export const RECRUIT_OPTIONS = [
+ { label: '๋ชจ๋ ํํธ', value: 'ALL' },
+ { label: 'ํ๋ก ํธ์๋', value: 'FRONTEND' },
+ { label: '๋ฐฑ์๋', value: 'BACKEND' },
+ { label: 'UX/UI ๋์์ธ', value: 'UI_UX' },
+ { label: 'AI', value: 'AI' },
+ { label: '์๋๋ก์ด๋', value: 'ANDROID' },
+ { label: 'iOS', value: 'IOS' },
+ { label: '๊ธฐํ', value: 'PLANNER' },
+ { label: '๋ง์ผํ
', value: 'MARKETER' },
+ { label: 'PM', value: 'PM' },
+ { label: 'ํด๋ผ์ฐ๋/์ธํ๋ผ', value: 'CLOUD_INFRA' },
+ { label: '๋ฐ๋ธ์ต์ค', value: 'DEV_OPS' },
+];
+
+export const DURATION_OPTIONS = [
+ { label: '์ ์ฒด', value: '' },
+ { label: '์จ๋ผ์ธ', value: 'ONLINE' },
+ { label: '์คํ๋ผ์ธ', value: 'OFFLINE' },
+ { label: '์จ/์คํ๋ผ์ธ', value: 'HYBRID' },
+];
+
+export const PERIOD_OPTIONS = [
+ { label: '1๊ฐ์ ์ด๋ด', value: 'WITHIN_1_MONTH' },
+ { label: '1~3๊ฐ์', value: 'ONE_TO_THREE_MONTHS' },
+ { label: '3~6๊ฐ์', value: 'THREE_TO_SIX_MONTHS' },
+ { label: '6๊ฐ์ ์ด์', value: 'OVER_SIX_MONTHS' },
+ { label: '๋ฏธ์ /ํ์์์ ', value: 'FLEXIBLE' },
+];
+
+export const RECRUIT_STATUS = [
+ { label: '๋ชจ์ง์ค', value: 'MATCHING' },
+ { label: '์ฐธ์ฌํ์ ', value: 'MATCHED' },
+ { label: '๋ชจ์ง๋ง๊ฐ', value: 'MATCH_FAILED' },
+];
+
+export const APPLICATION_STATUS = [
+ { label: '๋งค์นญ์ค', value: 'OPEN' },
+ { label: '๋งค์นญ์คํจ', value: 'CLOSED' },
+];
+
+export const WORKING_TIME_OPTIONS = [
+ { label: '์์นจ์ ์์
ํ๋๊ฒ ํธํด์', value: 'MORNING' },
+ { label: '๋ฎ์ ์์
ํ๋๊ฒ ํธํด์', value: 'AFTERNOON' },
+ { label: '๋ฐค์ ์์
ํ๋๊ฒ ํธํด์', value: 'EVENING' },
+ { label: '์๋ฒฝ์ ์์
ํ๋๊ฒ ํธํด์', value: 'DAWN' },
+];
+
+export const WORKING_VALUE_MAP: Record = {
+ ์์นจ: 'MORNING',
+ ๋ฎ: 'AFTERNOON',
+ ๋ฐค: 'EVENING',
+ ์๋ฒฝ: 'DAWN',
+};
+
+export const APPLIED_TEAMS = [
+ { label: '์ ์ฒด', value: '' },
+ { label: '๋งค์นญ์ค', value: 'MATCHING' },
+ { label: '๋งค์นญ ์ฑ๊ณต', value: 'MATCHED' },
+ { label: '๋งค์นญ ์คํจ', value: 'MATCH_FAILED' },
+];
+
+export const APPLICANT_STATUS = [
+ { label: '์ ์ฒด', value: '' },
+ { label: '๋ง๊ฐ๋ ๊ณต๊ณ ', value: 'CLOSED' },
+ { label: '์งํ์ค์ธ ๊ณต๊ณ ', value: 'OPEN' },
+];
+
+export const MY_TEAM_DROPDOWN = [
+ { label: '๋ชจ๋ ํํธ', value: '' },
+ { label: 'ํ๋ก ํธ์๋', value: 'FRONTEND' },
+ { label: '๋ฐฑ์๋', value: 'BACKEND' },
+ { label: 'UX/UI ๋์์ธ', value: 'UI_UX' },
+ { label: 'AI', value: 'AI' },
+ { label: '์๋๋ก์ด๋', value: 'ANDROID' },
+ { label: 'iOS', value: 'IOS' },
+ { label: '๊ธฐํ', value: 'PLANNER' },
+ { label: '๋ง์ผํ
', value: 'MARKETER' },
+ { label: 'PM', value: 'PM' },
+ { label: 'ํด๋ผ์ฐ๋/์ธํ๋ผ', value: 'CLOUD_INFRA' },
+ { label: '๋ฐ๋ธ์ต์ค', value: 'DEV_OPS' },
+];
diff --git a/src/constants/ModalList.ts b/src/constants/ModalList.ts
new file mode 100644
index 00000000..9325177b
--- /dev/null
+++ b/src/constants/ModalList.ts
@@ -0,0 +1,152 @@
+import ApplyModal from '@/components/modal/apply/ApplyModal';
+import ApplyCompleteModal from '@/components/modal/ApplyCompleteModal';
+import DeleteModal from '@/components/modal/DeleteModal';
+import PartnerModal from '@/components/modal/apply/PartnerModal';
+import TeamPsylogCompleteModal from '@/components/modal/TeamPsylogCompleteModal';
+import RecruitCompleteModal from '@/components/modal/RecruitCompleteModal';
+import { RecruitingPosition } from '@/types/project';
+import ApplicantFinishModal from '@/components/modal/ApplicantFinishModal';
+import ApplicateFinishModal from '@/components/modal/ApplicateFinishModal';
+import DeleteProfileModal from '@/components/modal/profile/DeleteProfileModal';
+import DeleteProfileCompleteModal from '@/components/modal/profile/DeleteProfileCompleteModal';
+import DeleteProfileError from '@/components/modal/profile/DeleteProfileError';
+import EditProfileCompleteModal from '@/components/modal/profile/EditProfileCompleteModal';
+import TeamPsylogAskModal from '@/components/modal/TeamPsylogAskModal';
+import { QuestionFormValues } from '@/libs/schemas/questionFormSchema';
+import ProfileDetailModal from '@/components/modal/profile/ProfileDetailModal';
+import { ResponseProfile } from '@/types/profile';
+import NotFinishModal from '@/components/modal/NotFinishModal';
+import ApplyModalMo from '@/components/modal/apply/ApplyModalMo';
+import { RequestReportComment } from '@/types/teampsylog';
+import ReportCommentModal from '@/components/modal/ReportCommentModal';
+import ReportCompleteModal from '@/components/modal/ReportCompleteModal';
+
+export interface BaseModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+export interface RecruitCompleteModalProps extends BaseModalProps {
+ onListClick?: () => void;
+ onDetailClick?: () => void;
+}
+
+export interface ApplyModalProps extends BaseModalProps {
+ postId?: number;
+ recruitingPositions?: RecruitingPosition[];
+}
+
+export interface ApplyCompleteModalProps extends BaseModalProps {
+ projectName?: string;
+ postId?: number;
+}
+
+export interface DeleteModalProps extends BaseModalProps {
+ postId?: number;
+ projectName?: string;
+ onConfirm?: () => void;
+}
+
+export interface PartnerModalProps extends BaseModalProps {
+ recruitingPostId: number;
+ applicationId: number;
+ profilePosition?: string;
+}
+
+export interface TeamPsylogCompleteModalProps extends BaseModalProps {
+ userName: string;
+ uuid: string;
+ onConfirm?: () => void;
+}
+
+export interface TeamPsylogAskModalProps extends BaseModalProps {
+ userName: string;
+ uuid: string;
+ formData: QuestionFormValues;
+}
+
+export interface ApplicateFinishModalProps extends BaseModalProps {
+ onConfirm?: () => void;
+ recruitingPostId?: number;
+}
+
+export interface DeleteProfileModalProps extends BaseModalProps {
+ profileId: number;
+ profileName: string;
+}
+
+export interface DeleteProfileCompleteModalProps extends BaseModalProps {
+ profileName: string;
+}
+
+export interface DeleteProfileErrorModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+export interface EditProfileCompleteModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+}
+
+export interface ProfileDetailModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ profile: ResponseProfile;
+}
+
+export interface ReportCommentModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ keywordCommentId: number;
+}
+
+export interface ReportCompleteModalProps {
+ isOpen: boolean;
+ onClose: () => void;
+ data: RequestReportComment;
+}
+
+export const MODAL_COMPONENTS = {
+ apply: ApplyModal,
+ applyMo: ApplyModalMo,
+ applyComplete: ApplyCompleteModal,
+ delete: DeleteModal,
+ partner: PartnerModal,
+ teamPsylogComplete: TeamPsylogCompleteModal,
+ teamPsylogAsk: TeamPsylogAskModal,
+ recruitComplete: RecruitCompleteModal,
+ applicateFinish: ApplicateFinishModal,
+ applicantFinish: ApplicantFinishModal,
+ profileDelete: DeleteProfileModal,
+ profileDeleteComplete: DeleteProfileCompleteModal,
+ profileDeleteError: DeleteProfileError,
+ profileEditComplete: EditProfileCompleteModal,
+ profileDetail: ProfileDetailModal,
+ notFinish: NotFinishModal,
+ reportComment: ReportCommentModal,
+ reportComplete: ReportCompleteModal,
+};
+
+export type ModalType = keyof typeof MODAL_COMPONENTS;
+
+export interface ModalPropsMap {
+ apply: ApplyModalProps;
+ applyMo: ApplyModalProps;
+ applyComplete: ApplyCompleteModalProps;
+ delete: DeleteModalProps;
+ partner: PartnerModalProps;
+ teamPsylogComplete: TeamPsylogCompleteModalProps;
+ teamPsylogAsk: TeamPsylogAskModalProps;
+ recruitComplete: RecruitCompleteModalProps;
+ applicateFinish: ApplicateFinishModalProps;
+ applicantFinish: BaseModalProps;
+ profileDelete: DeleteProfileModalProps;
+ profileDeleteComplete: DeleteProfileCompleteModalProps;
+ profileDeleteError: BaseModalProps;
+ profileEditComplete: BaseModalProps;
+ profileDetail: ProfileDetailModalProps;
+ notFinish: BaseModalProps;
+ reportComment: ReportCommentModalProps;
+ reportComplete: ReportCompleteModalProps;
+}
diff --git a/src/constants/Questions.ts b/src/constants/Questions.ts
new file mode 100644
index 00000000..19ef8480
--- /dev/null
+++ b/src/constants/Questions.ts
@@ -0,0 +1,20 @@
+export const QUESTION_SETS = {
+ set1: [
+ 'โ์ด๊ฑด ๋๋ ๋ฐฐ์ฐ๊ณ ์ถ๋ค"ํ๊ณ ๋๋ ์ ์ ํ๋๋ ๋ฌด์์ด์๋์?',
+ '์ ์ ๊ฐ์ฅ "ํต์ฌ ๋ฌด๊ธฐ"๊ฐ ๋ ๊ฑด? ๊ทธ ์ด์ ๋?',
+ 'ํ์
์ค ์ ์ ์์ฌ์ํต ๋ฐฉ์(ํ์ ์ฐธ์ฌ, ํผ๋๋ฐฑ, ์ผ์ ์กฐ์จ ๋ฑ)์ ๋ํด ์ด๋ป๊ฒ ๋๋ผ์
จ๋์?',
+ '์ ๊ฐ ์ผ์ ์ฐ์ ์์๋ฅผ ์ ํ๊ณ ์ถ์งํ๋ ๊ณผ์ ์ ์ด๋ ๋์?',
+ ],
+ set2: [
+ '์ ๊ฐ ํ ์ ์ฒด์ ๊ธ์ ์ ์ธ ์ํฅ์ ์ค ๊ตฌ์ฒด์ ์ธ ์์๊ฐ ์๋์?',
+ '์ ์ ํ์
ํ ๋ ํธํ๊ฑฐ๋ ๋ ๋ ํ๋ ์ด์ ๋?',
+ '์ ์ ๋ฌธ์ ํด๊ฒฐ ๋ฐฉ์ ์ค ์ธ์ ๊น์๋ ๊ฑด?',
+ '์ ์ ๋ค์ ํ๋ก์ ํธ๋ฅผ ๋ ๊ฐ์ดํ๊ฒ ๋๋ค๋ฉด, ๊ธฐ๋ํ๊ณ ์ถ์ ์๋ก์ด ๋ชจ์ต์?',
+ ],
+ set3: [
+ '์๊ธฐ์น ๋ชปํ ๋ฌธ์ ์ ๋์ํ๋ ์ ํ๋๋ ์ด๋ ๋์?',
+ '์ํต์ ์ธก๋ฉด์์ ์ ๊ฐ ๋ณด์ํ์ผ๋ฉด ํ๋ ์ ์ ๋ฌด์์ธ๊ฐ์?',
+ '์ ๊ฐ ๊ฐ์ ํ๋ฉด ๋ ๊ฐํด์ง ๋ถ๋ถ์?',
+ '์์ผ๋ก ์ ๊ฐ ๋ ์ฑ์ฅํ๊ธฐ ์ํด ๊ผญ ์๊ณ ๋์ด๊ฐ๋ฉด ์ข์ ์กฐ์ธ์ด ์๋ค๋ฉด ๋ฌด์์ผ๊น์?',
+ ],
+};
diff --git a/src/constants/Translate.ts b/src/constants/Translate.ts
new file mode 100644
index 00000000..4e4afe39
--- /dev/null
+++ b/src/constants/Translate.ts
@@ -0,0 +1,34 @@
+export const PROGRESS_WAY_KR: Record = {
+ ONLINE: '์จ๋ผ์ธ',
+ OFFLINE: '์คํ๋ผ์ธ',
+ HYBRID: '์จ/์คํ๋ผ์ธ',
+};
+
+export const PERIOD_KR: Record = {
+ WITHIN_1_MONTH: '1๊ฐ์ ์ด๋ด',
+ ONE_TO_THREE_MONTHS: '1~3๊ฐ์',
+ THREE_TO_SIX_MONTHS: '3~6๊ฐ์',
+ OVER_SIX_MONTHS: '6๊ฐ์ ์ด์',
+ FLEXIBLE: '๋ฏธ์ /ํ์ ์์ ',
+};
+
+export const POSITION_KR: Record = {
+ FRONTEND: 'ํ๋ก ํธ์๋',
+ BACKEND: '๋ฐฑ์๋',
+ UI_UX: 'UX/UI ๋์์ธ',
+ AI: 'AI',
+ ANDROID: '์๋๋ก์ด๋',
+ IOS: 'iOS',
+ PLANNER: '๊ธฐํ์',
+ MARKETER: '๋ง์ผํฐ',
+ PM: 'PM',
+ CLOUD_INFRA: 'ํด๋ผ์ฐ๋/์ธํ๋ผ',
+ DEV_OPS: '๋ฐ๋ธ์ต์ค',
+};
+
+export const WORKING_TIME_KR: Record = {
+ MORNING: '์์นจ',
+ AFTERNOON: '๋ฎ',
+ EVENING: '๋ฐค',
+ DAWN: '์๋ฒฝ',
+};
diff --git a/src/contexts/ModalContext.tsx b/src/contexts/ModalContext.tsx
new file mode 100644
index 00000000..c97630c8
--- /dev/null
+++ b/src/contexts/ModalContext.tsx
@@ -0,0 +1,59 @@
+'use client';
+
+import { createContext, useContext, useState, ReactNode } from 'react';
+import { MODAL_COMPONENTS, ModalPropsMap, ModalType } from '@/constants/ModalList';
+
+interface ModalContextProps {
+ modalType: ModalType | null;
+ modalProps: ModalPropsMap[keyof ModalPropsMap] | null;
+ openModal: (
+ type: T,
+ props?: Omit,
+ ) => void;
+ closeModal: () => void;
+}
+
+const ModalContext = createContext(null);
+
+export const ModalProvider = ({ children }: { children: ReactNode }) => {
+ const [modalType, setModalType] = useState(null);
+ const [modalProps, setModalProps] = useState(null);
+
+ const openModal = (
+ type: T,
+ props?: Omit,
+ ) => {
+ setModalType(type);
+ setModalProps(props as ModalPropsMap[T]);
+ };
+
+ const closeModal = () => {
+ setModalType(null);
+ setModalProps(null);
+ };
+
+ const renderModal = () => {
+ if (!modalType) return null;
+
+ const ModalComponent = MODAL_COMPONENTS[modalType] as React.ComponentType<
+ ModalPropsMap[typeof modalType]
+ >;
+
+ const props = modalProps as ModalPropsMap[typeof modalType];
+
+ return ;
+ };
+
+ return (
+
+ {children}
+ {renderModal()}
+
+ );
+};
+
+export const useModal = () => {
+ const context = useContext(ModalContext);
+ if (!context) throw new Error('useModal must be used within a ModalProvider');
+ return context;
+};
diff --git a/src/contexts/ToastContext.tsx b/src/contexts/ToastContext.tsx
new file mode 100644
index 00000000..b51ce803
--- /dev/null
+++ b/src/contexts/ToastContext.tsx
@@ -0,0 +1,71 @@
+'use client';
+
+import ToastContainer from '@/components/common/ToastContainer';
+import { createContext, useContext, useState, useCallback } from 'react';
+
+export interface ToastMessage {
+ id: string;
+ message: string;
+ type?: 'success' | 'error' | 'default';
+ duration?: number;
+ title?: string;
+}
+
+interface ToastContextType {
+ addToast: (msg: Omit) => void;
+}
+
+const ToastContext = createContext(null);
+
+export const ToastProvider = ({ children }: { children: React.ReactNode }) => {
+ const [toasts, setToasts] = useState([]);
+
+ const addToast = useCallback((toast: Omit) => {
+ const id = (() => {
+ try {
+ return crypto.randomUUID();
+ } catch {
+ return `${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
+ }
+ })();
+
+ const newToast: ToastMessage = {
+ id,
+ duration: 2000,
+ type: 'default',
+ ...toast,
+ };
+
+ setToasts((prev) => [...prev, newToast]);
+
+ setTimeout(() => {
+ setToasts((prev) => prev.filter((t) => t.id !== id));
+ }, newToast.duration);
+ }, []);
+
+ const isVisible = toasts.length > 0;
+
+ return (
+
+ {children}
+
+ {isVisible && (
+
+ )}
+
+
+ {toasts.map((t) => (
+
+
+
+ ))}
+
+
+ );
+};
+
+export const useToast = () => {
+ const ctx = useContext(ToastContext);
+ if (!ctx) throw new Error('ToastProvider๊ฐ ํ์ํฉ๋๋ค');
+ return ctx;
+};
diff --git a/src/hooks/mutation/useApplicateProject.ts b/src/hooks/mutation/useApplicateProject.ts
new file mode 100644
index 00000000..0a7d2041
--- /dev/null
+++ b/src/hooks/mutation/useApplicateProject.ts
@@ -0,0 +1,14 @@
+import { postApplication } from '@/libs/api/project';
+import { PostApplication } from '@/types/project';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+
+export const useApplicateProject = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (project: PostApplication) => postApplication(project),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['applications'] });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useCloseProject.ts b/src/hooks/mutation/useCloseProject.ts
new file mode 100644
index 00000000..1b791239
--- /dev/null
+++ b/src/hooks/mutation/useCloseProject.ts
@@ -0,0 +1,15 @@
+import { closeRecruitingPost } from '@/libs/api/application';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+
+export const useCloseProject = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (recruitingPostId: number) => closeRecruitingPost(recruitingPostId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ['projects'],
+ });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useCreateProfile.ts b/src/hooks/mutation/useCreateProfile.ts
new file mode 100644
index 00000000..f66b3250
--- /dev/null
+++ b/src/hooks/mutation/useCreateProfile.ts
@@ -0,0 +1,16 @@
+import { createProfile } from '@/libs/api/profile';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+
+export const useCreateProfile = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: createProfile,
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['profile'] });
+ },
+ onError: (error) => {
+ console.error('ํ๋กํ ์์ฑ ์ค ์ค๋ฅ ๋ฐ์:', error);
+ },
+ });
+};
diff --git a/src/hooks/mutation/useCreateProject.ts b/src/hooks/mutation/useCreateProject.ts
new file mode 100644
index 00000000..0050e979
--- /dev/null
+++ b/src/hooks/mutation/useCreateProject.ts
@@ -0,0 +1,15 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { postProject } from '@/libs/api/project';
+import { CreateProject } from '@/types/project';
+
+export const useCreateProject = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (project: CreateProject) => postProject(project),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['projects'] });
+ // TODO: ์ฑ๊ณต ์ ์ฒ๋ฆฌ ๋
ผ์
+ },
+ });
+};
diff --git a/src/hooks/mutation/useDeleteProfile.ts b/src/hooks/mutation/useDeleteProfile.ts
new file mode 100644
index 00000000..d315c4c0
--- /dev/null
+++ b/src/hooks/mutation/useDeleteProfile.ts
@@ -0,0 +1,18 @@
+'use client';
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { deleteProfile } from '@/libs/api/profile';
+
+export const useDeleteProfile = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (profileId: number) => deleteProfile(profileId),
+
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ['profile'],
+ });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useDeleteProfileImage.ts b/src/hooks/mutation/useDeleteProfileImage.ts
new file mode 100644
index 00000000..fa0dad0c
--- /dev/null
+++ b/src/hooks/mutation/useDeleteProfileImage.ts
@@ -0,0 +1,21 @@
+'use client';
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { deleteProfileImage } from '@/libs/api/profile';
+
+export const useDeleteProfileImage = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (profileId: number) => deleteProfileImage(profileId),
+
+ onSuccess: (_, profileId) => {
+ queryClient.invalidateQueries({
+ queryKey: ['profile', profileId],
+ });
+ queryClient.invalidateQueries({
+ queryKey: ['profile'],
+ });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useDeleteProject.ts b/src/hooks/mutation/useDeleteProject.ts
new file mode 100644
index 00000000..ae4d4d64
--- /dev/null
+++ b/src/hooks/mutation/useDeleteProject.ts
@@ -0,0 +1,15 @@
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { deleteProject } from '@/libs/api/project';
+
+export const useDeleteProject = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (postId: number) => deleteProject(postId),
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ['projects'],
+ });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useReportComment.ts b/src/hooks/mutation/useReportComment.ts
new file mode 100644
index 00000000..447a2bca
--- /dev/null
+++ b/src/hooks/mutation/useReportComment.ts
@@ -0,0 +1,17 @@
+import { postReportComment } from '@/libs/api/teampsylog';
+import { RequestReportComment } from '@/types/teampsylog';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+
+export const usePostReportComment = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (data: RequestReportComment) => postReportComment(data),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['keywordComment'] });
+ },
+ onError: (error) => {
+ console.error('๋๊ธ ์ ๊ณ ์ค ์ค๋ฅ ๋ฐ์:', error);
+ },
+ });
+};
diff --git a/src/hooks/mutation/useTeamficialLog.ts b/src/hooks/mutation/useTeamficialLog.ts
new file mode 100644
index 00000000..bfea10ee
--- /dev/null
+++ b/src/hooks/mutation/useTeamficialLog.ts
@@ -0,0 +1,19 @@
+'use client';
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { postTeamficialLog } from '@/libs/api/teampsylog';
+import { RequestTeamficialLog } from '@/types/teampsylog';
+
+export const useTeamficialLog = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (data: RequestTeamficialLog) => postTeamficialLog(data),
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: ['teamficialLog'] });
+ },
+ onError: (error) => {
+ console.error('ํํผ์
๋ก ์์ฑ ์ค ์ค๋ฅ ๋ฐ์:', error);
+ },
+ });
+};
diff --git a/src/hooks/mutation/useUpdateApplicant.ts b/src/hooks/mutation/useUpdateApplicant.ts
new file mode 100644
index 00000000..a17e9d2f
--- /dev/null
+++ b/src/hooks/mutation/useUpdateApplicant.ts
@@ -0,0 +1,16 @@
+import { updateApplicationStatus } from '@/libs/api/application';
+import { UpdateApplicationStatus } from '@/types/application';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+
+export const useUpdateApplicant = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (status: UpdateApplicationStatus) => updateApplicationStatus(status),
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: ['currentApplicants'],
+ });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useUpdateHeadKeywords.ts b/src/hooks/mutation/useUpdateHeadKeywords.ts
new file mode 100644
index 00000000..5ff3191b
--- /dev/null
+++ b/src/hooks/mutation/useUpdateHeadKeywords.ts
@@ -0,0 +1,16 @@
+import { putHeadKeywords } from '@/libs/api/teampsylog';
+import { RequestHeadKeyword } from '@/types/teampsylog';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+
+export const useUpdateHeadKeywords = () => {
+ const queryClient = useQueryClient();
+ return useMutation({
+ mutationFn: ({ profileId, keywordId, oldHeadKeywordId }: RequestHeadKeyword) =>
+ putHeadKeywords({ profileId, keywordId, oldHeadKeywordId }),
+ onSuccess: (_, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: ['keyword', variables.profileId],
+ });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useUpdateProfile.ts b/src/hooks/mutation/useUpdateProfile.ts
new file mode 100644
index 00000000..4b126605
--- /dev/null
+++ b/src/hooks/mutation/useUpdateProfile.ts
@@ -0,0 +1,41 @@
+'use client';
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { updateProfile } from '@/libs/api/profile';
+import { ResponseProfile } from '@/types/profile';
+
+export const useUpdateProfile = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: (params: {
+ profileId: number;
+ profileName: string | null;
+ workingTime: string | null;
+ links: string[];
+ contactWay: string | null;
+ }) => {
+ const { profileId, profileName, workingTime, contactWay, links } = params;
+
+ const convertedPayload = {
+ profileName: profileName?.trim() === '' ? null : profileName,
+ workingTime: workingTime?.trim() === '' ? null : workingTime,
+ contactWay: contactWay?.trim() === '' ? null : contactWay,
+
+ links:
+ links.length === 0 || links.every((l) => l.trim() === '')
+ ? ['']
+ : links.map((l) => (l.trim() === '' ? '' : l.trim())),
+ };
+
+ return updateProfile(profileId, convertedPayload);
+ },
+
+ onSuccess: (data: ResponseProfile) => {
+ queryClient.invalidateQueries({
+ queryKey: ['profile', data.profileId],
+ });
+ queryClient.invalidateQueries({ queryKey: ['profile'] });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useUpdateProject.ts b/src/hooks/mutation/useUpdateProject.ts
new file mode 100644
index 00000000..2ca1e4f5
--- /dev/null
+++ b/src/hooks/mutation/useUpdateProject.ts
@@ -0,0 +1,17 @@
+import { patchProject } from '@/libs/api/project';
+import { Project } from '@/types/project';
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+
+export const useUpdateProject = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: ({ postId, project }: { postId: number; project: Project }) =>
+ patchProject(project, postId),
+ onSuccess: (_, variables) => {
+ queryClient.invalidateQueries({
+ queryKey: ['project', variables.postId],
+ });
+ },
+ });
+};
diff --git a/src/hooks/mutation/useUploadProfileImage.ts b/src/hooks/mutation/useUploadProfileImage.ts
new file mode 100644
index 00000000..572f9965
--- /dev/null
+++ b/src/hooks/mutation/useUploadProfileImage.ts
@@ -0,0 +1,28 @@
+'use client';
+
+import { useMutation, useQueryClient } from '@tanstack/react-query';
+import { getPresignedUrl, uploadToS3 } from '@/libs/api/image';
+import { updateProfileImage } from '@/libs/api/profile';
+
+export const useUploadProfileImage = () => {
+ const queryClient = useQueryClient();
+
+ return useMutation({
+ mutationFn: async ({ profileId, file }: { profileId: number; file: File }) => {
+ const { preSignedUrl, objectKey } = await getPresignedUrl(file.name);
+
+ const success = await uploadToS3(preSignedUrl, file);
+ if (!success) throw new Error('Failed to upload to S3');
+
+ await updateProfileImage(profileId, objectKey);
+
+ return objectKey;
+ },
+
+ onSuccess: (_, { profileId }) => {
+ queryClient.invalidateQueries({
+ queryKey: ['profile', profileId],
+ });
+ },
+ });
+};
diff --git a/src/hooks/queries/useApplicant.ts b/src/hooks/queries/useApplicant.ts
new file mode 100644
index 00000000..225704b3
--- /dev/null
+++ b/src/hooks/queries/useApplicant.ts
@@ -0,0 +1,19 @@
+'use client';
+
+import { useQuery } from '@tanstack/react-query';
+import { getCurrentApplicants, getMyApplications } from '@/libs/api/mypage/applicant';
+import { ResponseApplications, ResponseCurrentApplicants } from '@/types/applicant';
+
+export const useMyApplications = (applicationStatus: string, page: number, size: number = 6) => {
+ return useQuery({
+ queryKey: ['myApplications', applicationStatus, page, size],
+ queryFn: () => getMyApplications(applicationStatus, page, size),
+ });
+};
+
+export const useCurrentApplicants = (recruitingStatus: string, page: number, size: number = 6) => {
+ return useQuery({
+ queryKey: ['currentApplicants', recruitingStatus, page, size],
+ queryFn: () => getCurrentApplicants(recruitingStatus, page, size),
+ });
+};
diff --git a/src/hooks/queries/useApplicate.ts b/src/hooks/queries/useApplicate.ts
new file mode 100644
index 00000000..380680b8
--- /dev/null
+++ b/src/hooks/queries/useApplicate.ts
@@ -0,0 +1,16 @@
+import { getApplicantProfile } from '@/libs/api/application';
+import { ApplicationResponse } from '@/types/application';
+import { useQuery } from '@tanstack/react-query';
+
+export const useGetApplicantProfile = ({
+ recruitingPostId,
+ applicationId,
+}: {
+ recruitingPostId: number;
+ applicationId: number;
+}) => {
+ return useQuery({
+ queryKey: ['applicantProfile', recruitingPostId, applicationId],
+ queryFn: () => getApplicantProfile(recruitingPostId, applicationId),
+ });
+};
diff --git a/src/hooks/queries/useConfirmedProfiles.ts b/src/hooks/queries/useConfirmedProfiles.ts
new file mode 100644
index 00000000..09ee448c
--- /dev/null
+++ b/src/hooks/queries/useConfirmedProfiles.ts
@@ -0,0 +1,9 @@
+import { getConfirmedProfile } from '@/libs/api/mypage/applicant';
+import { useQuery } from '@tanstack/react-query';
+
+export const useConfirmedProfiles = (postId: number, position: string) => {
+ return useQuery({
+ queryKey: ['confirmed-profiles', postId, position],
+ queryFn: () => getConfirmedProfile(postId, position),
+ });
+};
diff --git a/src/hooks/queries/useDashboard.ts b/src/hooks/queries/useDashboard.ts
new file mode 100644
index 00000000..6b6ae21f
--- /dev/null
+++ b/src/hooks/queries/useDashboard.ts
@@ -0,0 +1,12 @@
+'use client';
+
+import { useQuery } from '@tanstack/react-query';
+import { getDashboard } from '@/libs/api/mypage/dashboard';
+import { ResponseDashboard } from '@/types/project';
+
+export const useDashboard = () => {
+ return useQuery({
+ queryKey: ['dashboard'],
+ queryFn: getDashboard,
+ });
+};
diff --git a/src/hooks/queries/useKeyword.ts b/src/hooks/queries/useKeyword.ts
new file mode 100644
index 00000000..68b027ce
--- /dev/null
+++ b/src/hooks/queries/useKeyword.ts
@@ -0,0 +1,79 @@
+import {
+ getKeywordComments,
+ getKeywordList,
+ getKeywords,
+ getRandomKeywords,
+} from '@/libs/api/teampsylog';
+import {
+ RequestKeyword,
+ RequestKeywordComment,
+ ResponseKeyword,
+ ResponseKeywordComment,
+ ResponseKeywordList,
+ ResponseRandomKeywords,
+} from '@/types/teampsylog';
+import { useInfiniteQuery, useQuery } from '@tanstack/react-query';
+
+export const useGetKeyword = ({ profileId }: { profileId: number }) => {
+ return useQuery({
+ queryKey: ['keyword', profileId],
+ queryFn: () => getKeywords(profileId),
+ enabled: !!profileId,
+ });
+};
+
+export const useGetKeywordList = ({ userId, page = 0, size = 3 }: RequestKeyword) => {
+ return useQuery({
+ queryKey: ['keywordList', userId, page, size],
+ queryFn: () => getKeywordList({ userId, page, size }),
+ enabled: !!userId,
+ });
+};
+
+export const useGetKeywordComment = ({ keywordId, page = 0, size = 4 }: RequestKeywordComment) => {
+ return useQuery({
+ queryKey: ['keywordComment', keywordId, page, size],
+ queryFn: () =>
+ getKeywordComments({
+ keywordId,
+ page,
+ size,
+ }),
+ enabled: false,
+ });
+};
+
+export const useInfiniteKeywordComment = ({
+ keywordId,
+ size = 4,
+}: {
+ keywordId: number;
+ size?: number;
+}) => {
+ return useInfiniteQuery({
+ queryKey: ['keywordComment', keywordId],
+ queryFn: ({ pageParam = 0 }) =>
+ getKeywordComments({
+ keywordId,
+ page: Number(pageParam),
+ size,
+ }),
+ initialPageParam: 0,
+ getNextPageParam: (lastPage, allPages) => {
+ // lastPage.hasNext๊ฐ true๋ฉด ๋ค์ ํ์ด์ง ๋ฒํธ ๋ฐํ
+ if (lastPage.hasNext) {
+ return allPages.length; // ๋ค์ page ๋ฒํธ
+ }
+ return undefined;
+ },
+ enabled: !!keywordId,
+ });
+};
+
+export const useGetRandomKeywords = (requesterUuid: string) => {
+ return useQuery({
+ queryKey: ['randomKeywords', requesterUuid],
+ queryFn: () => getRandomKeywords(requesterUuid),
+ enabled: !!requesterUuid,
+ });
+};
diff --git a/src/hooks/queries/useMyTeams.ts b/src/hooks/queries/useMyTeams.ts
new file mode 100644
index 00000000..a06ca961
--- /dev/null
+++ b/src/hooks/queries/useMyTeams.ts
@@ -0,0 +1,11 @@
+'use client';
+
+import { getMyTeams } from '@/libs/api/mypage/applicant';
+import { useQuery } from '@tanstack/react-query';
+
+export function useMyTeams(page: number = 0, size: number = 6) {
+ return useQuery({
+ queryKey: ['myTeams', page, size],
+ queryFn: () => getMyTeams(page, size),
+ });
+}
diff --git a/src/hooks/queries/useProfile.ts b/src/hooks/queries/useProfile.ts
new file mode 100644
index 00000000..053f6725
--- /dev/null
+++ b/src/hooks/queries/useProfile.ts
@@ -0,0 +1,25 @@
+import { getProfileList, getSingleProfile, getUuidProfileList } from '@/libs/api/profile';
+import { ResponseProfile } from '@/types/profile';
+import { useQuery } from '@tanstack/react-query';
+
+export const useGetProfile = ({ profileId }: { profileId: number }) => {
+ return useQuery({
+ queryKey: ['profile', profileId],
+ queryFn: () => getSingleProfile(profileId),
+ });
+};
+
+export const useGetProfileList = () => {
+ return useQuery({
+ queryKey: ['profile'],
+ queryFn: () => getProfileList(),
+ });
+};
+
+export const useGetUuidProfileList = (userUuid: string) => {
+ return useQuery({
+ queryKey: ['uuidProfiles', userUuid],
+ queryFn: () => getUuidProfileList(userUuid),
+ enabled: !!userUuid,
+ });
+};
diff --git a/src/hooks/queries/useProject.ts b/src/hooks/queries/useProject.ts
new file mode 100644
index 00000000..dae4599e
--- /dev/null
+++ b/src/hooks/queries/useProject.ts
@@ -0,0 +1,31 @@
+import { getCurrentApplicants } from '@/libs/api/application';
+import { getSingleProject } from '@/libs/api/project';
+import { ResponseApplicantsDetail } from '@/types/application';
+import { ResponseProject } from '@/types/project';
+import { PositionType } from '@/utils/position';
+import { useQuery } from '@tanstack/react-query';
+
+export const useGetProject = (
+ { postId }: { postId: number },
+ p0: { refetchOnMount: boolean; refetchOnWindowFocus: boolean },
+) => {
+ return useQuery({
+ queryKey: ['project', postId],
+ queryFn: () => getSingleProject(postId),
+ ...p0,
+ });
+};
+
+export const useGetProjectApplicants = ({
+ postId,
+ position,
+}: {
+ postId: number;
+ position?: PositionType;
+}) => {
+ return useQuery({
+ queryKey: ['projectApplicants', postId, position],
+ queryFn: () => getCurrentApplicants(postId, position),
+ retry: 0,
+ });
+};
diff --git a/src/hooks/queries/useRecruitingPosts.ts b/src/hooks/queries/useRecruitingPosts.ts
new file mode 100644
index 00000000..26d74640
--- /dev/null
+++ b/src/hooks/queries/useRecruitingPosts.ts
@@ -0,0 +1,53 @@
+'use client';
+
+import { useQuery, useInfiniteQuery } from '@tanstack/react-query';
+import { getRecruitingPosts } from '@/libs/api/recruitingPosts';
+import { PagedProjects } from '@/types/project';
+
+export interface Filters {
+ duration: string;
+ recruit: string;
+ onlyOpen: boolean;
+}
+
+export const useRecruitingPosts = (filters: Filters, pageNumber: number, pageSize = 9) => {
+ return useQuery({
+ queryKey: ['recruitingPosts', JSON.stringify(filters), pageNumber, pageSize],
+ queryFn: async () => {
+ const result = await getRecruitingPosts({
+ status: filters.onlyOpen ? 'OPEN' : undefined,
+ position: filters.recruit || undefined,
+ progressWay: filters.duration || undefined,
+ page: pageNumber - 1,
+ size: pageSize,
+ sort: [''],
+ });
+ console.log(result);
+ return result;
+ },
+ });
+};
+
+export const useInfiniteRecruitingPosts = (filters: Filters, pageSize = 10) => {
+ return useInfiniteQuery({
+ queryKey: ['recruitingPostsInfinite', JSON.stringify(filters), pageSize],
+ queryFn: async ({ pageParam = 0 }) => {
+ const result = await getRecruitingPosts({
+ status: filters.onlyOpen ? 'OPEN' : undefined,
+ position: filters.recruit || undefined,
+ progressWay: filters.duration || undefined,
+ page: pageParam as number,
+ size: pageSize,
+ sort: [''],
+ });
+ return result;
+ },
+ initialPageParam: 0,
+ getNextPageParam: (lastPage) => {
+ if (!lastPage.last) {
+ return lastPage.number + 1;
+ }
+ return undefined;
+ },
+ });
+};
diff --git a/src/hooks/queries/useRequesterInfo.ts b/src/hooks/queries/useRequesterInfo.ts
new file mode 100644
index 00000000..b5d5061c
--- /dev/null
+++ b/src/hooks/queries/useRequesterInfo.ts
@@ -0,0 +1,12 @@
+'use client';
+
+import { useQuery } from '@tanstack/react-query';
+import { getRequesterInfo } from '@/libs/api/teampsylog';
+
+export const useRequesterInfo = (uuid: string, options?: { enabled?: boolean }) => {
+ return useQuery({
+ queryKey: ['requesterInfo', uuid],
+ queryFn: () => getRequesterInfo(uuid),
+ enabled: !!uuid && (options?.enabled ?? true),
+ });
+};
diff --git a/src/hooks/useIsAuthor.ts b/src/hooks/useIsAuthor.ts
new file mode 100644
index 00000000..8d946f0d
--- /dev/null
+++ b/src/hooks/useIsAuthor.ts
@@ -0,0 +1,30 @@
+import { useState, useEffect } from 'react';
+
+export const useIsAuthor = (writerUserId: number) => {
+ const [isAuthor, setIsAuthor] = useState(false);
+
+ useEffect(() => {
+ try {
+ const userString = localStorage.getItem('user');
+ if (!userString) {
+ setIsAuthor(false);
+ return;
+ }
+
+ const userData = JSON.parse(userString);
+ const currentUserId = userData?.state?.userId;
+
+ if (!currentUserId) {
+ setIsAuthor(false);
+ return;
+ }
+
+ setIsAuthor(Number(currentUserId) === Number(writerUserId));
+ } catch (error) {
+ console.error('localStorage ํ์ฑ ์๋ฌ:', error);
+ setIsAuthor(false);
+ }
+ }, [writerUserId]);
+
+ return isAuthor;
+};
diff --git a/src/hooks/useIsMobile.ts b/src/hooks/useIsMobile.ts
new file mode 100644
index 00000000..82f1eb1c
--- /dev/null
+++ b/src/hooks/useIsMobile.ts
@@ -0,0 +1,19 @@
+'use client';
+
+import { useEffect, useState } from 'react';
+
+export function useIsMobile(breakpoint: number = 640) {
+ const [isMobile, setIsMobile] = useState(undefined);
+
+ useEffect(() => {
+ const checkMobile = () => {
+ setIsMobile(window.innerWidth < breakpoint);
+ };
+
+ checkMobile();
+ window.addEventListener('resize', checkMobile);
+ return () => window.removeEventListener('resize', checkMobile);
+ }, [breakpoint]);
+
+ return isMobile ?? false;
+}
diff --git a/src/hooks/useMediaQuery.ts b/src/hooks/useMediaQuery.ts
new file mode 100644
index 00000000..025c37ad
--- /dev/null
+++ b/src/hooks/useMediaQuery.ts
@@ -0,0 +1,20 @@
+import { useEffect, useState } from 'react';
+
+export const useMediaQuery = (query: string): boolean => {
+ const [matches, setMatches] = useState(() => {
+ if (typeof window === 'undefined') return false;
+ return window.matchMedia(query).matches;
+ });
+ useEffect(() => {
+ const media = window.matchMedia(query);
+
+ setMatches(media.matches);
+
+ const listener = () => setMatches(media.matches);
+ media.addEventListener('change', listener);
+
+ return () => media.removeEventListener('change', listener);
+ }, [query]);
+
+ return matches;
+};
diff --git a/src/hooks/useOutsideClick.ts b/src/hooks/useOutsideClick.ts
new file mode 100644
index 00000000..cdec40a1
--- /dev/null
+++ b/src/hooks/useOutsideClick.ts
@@ -0,0 +1,22 @@
+import { useEffect } from 'react';
+
+type Handler = (event: MouseEvent) => void;
+
+const useOutsideClick = (
+ ref: React.RefObject,
+ handler: Handler,
+) => {
+ useEffect(() => {
+ const listener = (event: MouseEvent) => {
+ if (!ref.current || ref.current.contains(event.target as Node)) return;
+ handler(event);
+ };
+
+ document.addEventListener('mousedown', listener);
+ return () => {
+ document.removeEventListener('mousedown', listener);
+ };
+ }, [ref, handler]);
+};
+
+export default useOutsideClick;
diff --git a/src/hooks/usePreventNavigation.ts b/src/hooks/usePreventNavigation.ts
new file mode 100644
index 00000000..c44ba64d
--- /dev/null
+++ b/src/hooks/usePreventNavigation.ts
@@ -0,0 +1,42 @@
+'use client';
+
+import { useEffect } from 'react';
+import { useRouter, usePathname } from 'next/navigation';
+
+export const usePreventNavigation = (
+ shouldPrevent: boolean,
+ onNavigationAttempt: (navigate: () => void) => void,
+) => {
+ const router = useRouter();
+ const pathname = usePathname();
+
+ useEffect(() => {
+ if (!shouldPrevent) return;
+
+ const handleClick = (e: MouseEvent) => {
+ const target = e.target as HTMLElement;
+ const link = target.closest('a');
+
+ if (link && link.href) {
+ const url = new URL(link.href);
+ const isSameOrigin = url.origin === window.location.origin;
+ const isDifferentPath = url.pathname !== pathname;
+
+ if (isSameOrigin && isDifferentPath) {
+ e.preventDefault();
+ e.stopPropagation();
+
+ onNavigationAttempt(() => {
+ router.push(url.pathname + url.search + url.hash);
+ });
+ }
+ }
+ };
+
+ document.addEventListener('click', handleClick, true);
+
+ return () => {
+ document.removeEventListener('click', handleClick, true);
+ };
+ }, [shouldPrevent, pathname, router, onNavigationAttempt]);
+};
diff --git a/src/hooks/useRecruitNavigationGuard.ts b/src/hooks/useRecruitNavigationGuard.ts
new file mode 100644
index 00000000..5f96b2d7
--- /dev/null
+++ b/src/hooks/useRecruitNavigationGuard.ts
@@ -0,0 +1,46 @@
+import { useEffect } from 'react';
+import { useModal } from '@/contexts/ModalContext';
+
+// ์ดํ ๋ฐฉ์ง ๋ฐ NotFinishModal ์ฒ๋ฆฌ ์ ์ฉ ์ปค์คํ
ํ
+export function useRecruitNavigationGuard(
+ isActive: boolean,
+ setIsSubmitting: (v: boolean) => void,
+) {
+ const { openModal, closeModal } = useModal();
+
+ // ๋ค๋ก๊ฐ๊ธฐ ๋ฑ popstate ์ ๋ชจ๋ฌ
+ useEffect(() => {
+ if (!isActive) return;
+
+ // ๋ง์ดํธ ์ ํ ๋ฒ๋ง pushState
+ window.history.pushState(null, '', window.location.pathname);
+
+ const handlePopState = () => {
+ openModal('notFinish', {
+ onConfirm: () => {
+ setIsSubmitting(true);
+ closeModal();
+ window.history.back();
+ },
+ });
+ };
+
+ window.addEventListener('popstate', handlePopState);
+ return () => {
+ window.removeEventListener('popstate', handlePopState);
+ };
+ }, [isActive, openModal, closeModal, setIsSubmitting]);
+
+ // ์๋ก๊ณ ์นจ/๋ซ๊ธฐ ์ ๋ธ๋ผ์ฐ์ confirm
+ useEffect(() => {
+ if (!isActive) return;
+ const handleBeforeUnload = (e: BeforeUnloadEvent) => {
+ e.preventDefault();
+ e.returnValue = '';
+ };
+ window.addEventListener('beforeunload', handleBeforeUnload);
+ return () => {
+ window.removeEventListener('beforeunload', handleBeforeUnload);
+ };
+ }, [isActive]);
+}
diff --git a/src/hooks/useSwipeableCards.ts b/src/hooks/useSwipeableCards.ts
new file mode 100644
index 00000000..8b29ce51
--- /dev/null
+++ b/src/hooks/useSwipeableCards.ts
@@ -0,0 +1,60 @@
+'use client';
+
+import { useState, useRef, useEffect } from 'react';
+
+interface UseSwipeableCardsProps {
+ items: T[];
+ maxItems?: number;
+ minSwipeDistance?: number;
+}
+
+export const useSwipeableCards = ({
+ items,
+ maxItems = 3,
+ minSwipeDistance = 50,
+}: UseSwipeableCardsProps) => {
+ const [currentIndex, setCurrentIndex] = useState(0);
+ const touchStartX = useRef(null);
+ const touchEndX = useRef(null);
+
+ const displayItems = items.slice(0, maxItems);
+
+ useEffect(() => {
+ const maxIndex = Math.max(displayItems.length - 1, 0);
+ if (currentIndex > maxIndex) {
+ setCurrentIndex(maxIndex);
+ }
+ }, [displayItems.length, currentIndex]);
+
+ const onTouchStart = (e: React.TouchEvent) => {
+ touchEndX.current = null;
+ touchStartX.current = e.targetTouches[0].clientX;
+ };
+
+ const onTouchMove = (e: React.TouchEvent) => {
+ touchEndX.current = e.targetTouches[0].clientX;
+ };
+
+ const onTouchEnd = () => {
+ if (!touchStartX.current || !touchEndX.current) return;
+ const distance = touchStartX.current - touchEndX.current;
+ const isLeftSwipe = distance > minSwipeDistance;
+ const isRightSwipe = distance < -minSwipeDistance;
+
+ if (isLeftSwipe && currentIndex < displayItems.length - 1) {
+ setCurrentIndex(currentIndex + 1);
+ }
+ if (isRightSwipe && currentIndex > 0) {
+ setCurrentIndex(currentIndex - 1);
+ }
+ };
+
+ return {
+ currentIndex,
+ setCurrentIndex,
+ displayItems,
+ onTouchStart,
+ onTouchMove,
+ onTouchEnd,
+ };
+};
diff --git a/src/hooks/useTruncatedTooltip.ts b/src/hooks/useTruncatedTooltip.ts
new file mode 100644
index 00000000..2a3f2f1f
--- /dev/null
+++ b/src/hooks/useTruncatedTooltip.ts
@@ -0,0 +1,83 @@
+import { useEffect, useRef, useState } from 'react';
+
+interface UseTruncatedTooltipOptions {
+ text: string;
+ maxLength?: number;
+ isMobile?: boolean;
+}
+
+interface TooltipPos {
+ left: number;
+ top: number;
+}
+
+export function useTruncatedTooltip({
+ text,
+ maxLength = 5,
+ isMobile = false,
+}: UseTruncatedTooltipOptions) {
+ const [showTooltip, setShowTooltip] = useState(false);
+ const [tooltipPos, setTooltipPos] = useState(null);
+ const ref = useRef(null);
+ const timeoutRef = useRef(null);
+
+ const getTextLengthWithoutSpaces = (t: string) => t.replace(/\s/g, '').length;
+ const limit = typeof maxLength === 'number' ? maxLength : 5;
+ const isTruncated = getTextLengthWithoutSpaces(text) > limit;
+
+ let displayText = text;
+ if (isTruncated) {
+ let count = 0;
+ displayText = '';
+ for (let i = 0; i < text.length; i++) {
+ if (text[i] !== ' ') count++;
+ if (count > limit) {
+ displayText += '...';
+ break;
+ }
+ displayText += text[i];
+ }
+ }
+
+ useEffect(() => {
+ return () => {
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
+ };
+ }, []);
+
+ const handleMouseEnter = (e: React.MouseEvent) => {
+ if (!isMobile && isTruncated) {
+ const rect = (e.target as HTMLElement).getBoundingClientRect();
+ setTooltipPos({ left: rect.left + window.scrollX, top: rect.bottom + window.scrollY });
+ setShowTooltip(true);
+ }
+ };
+ const handleMouseLeave = () => {
+ setShowTooltip(false);
+ setTooltipPos(null);
+ };
+
+ const handleTouch = (e: React.TouchEvent) => {
+ if (isMobile && isTruncated) {
+ const rect = (e.target as HTMLElement).getBoundingClientRect();
+ setTooltipPos({ left: rect.left + window.scrollX, top: rect.bottom + window.scrollY });
+ setShowTooltip(true);
+ if (timeoutRef.current) clearTimeout(timeoutRef.current);
+ timeoutRef.current = setTimeout(() => {
+ setShowTooltip(false);
+ setTooltipPos(null);
+ }, 1500);
+ }
+ };
+
+ return {
+ ref,
+ showTooltip,
+ tooltipPos,
+ displayText,
+ isTruncated,
+ handleMouseEnter,
+ handleMouseLeave,
+ handleTouch,
+ };
+}
diff --git a/src/libs/api/api.ts b/src/libs/api/api.ts
new file mode 100644
index 00000000..40458094
--- /dev/null
+++ b/src/libs/api/api.ts
@@ -0,0 +1,69 @@
+import axios from 'axios';
+import qs from 'qs';
+
+const api = axios.create({
+ baseURL: process.env.NEXT_PUBLIC_API_BASE_URL,
+ paramsSerializer: (params) => qs.stringify(params, { encode: false }),
+ headers: { 'Content-Type': 'application/json' },
+});
+
+const requestNewTokens = async () => {
+ const refreshToken = localStorage.getItem('refreshToken');
+ if (!refreshToken) throw new Error('No refresh token');
+
+ const res = await axios.get(`${process.env.NEXT_PUBLIC_API_BASE_URL}/auth/refresh-token`, {
+ headers: {
+ Authorization: `Bearer ${refreshToken}`,
+ },
+ });
+
+ if (!res.data?.isSuccess) {
+ throw new Error('Refresh failed');
+ }
+
+ const { accessToken, refreshToken: newRefresh } = res.data.result;
+
+ localStorage.setItem('accessToken', accessToken);
+ localStorage.setItem('refreshToken', newRefresh);
+
+ return accessToken;
+};
+
+api.interceptors.request.use(
+ (config) => {
+ const accessToken = localStorage.getItem('accessToken');
+ if (accessToken) {
+ config.headers.Authorization = `Bearer ${accessToken}`;
+ }
+ return config;
+ },
+ (error) => Promise.reject(error),
+);
+
+api.interceptors.response.use(
+ (response) => response,
+ async (error) => {
+ const originalRequest = error.config;
+
+ if (error.response?.status === 401 && !originalRequest._retry) {
+ originalRequest._retry = true;
+
+ try {
+ const newAccessToken = await requestNewTokens();
+
+ originalRequest.headers.Authorization = `Bearer ${newAccessToken}`;
+
+ return api(originalRequest);
+ } catch (refreshError) {
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ window.location.href = '/login';
+ return Promise.reject(refreshError);
+ }
+ }
+
+ return Promise.reject(error);
+ },
+);
+
+export default api;
diff --git a/src/libs/api/application.ts b/src/libs/api/application.ts
new file mode 100644
index 00000000..4755dfa4
--- /dev/null
+++ b/src/libs/api/application.ts
@@ -0,0 +1,74 @@
+import {
+ ApplicationResponse,
+ ResponseApplicantsDetail,
+ UpdateApplicationStatus,
+} from '@/types/application';
+import api from './api';
+import { CommonResponse } from '@/types/common';
+import { PositionType } from '@/utils/position';
+
+export async function getCurrentApplicants(
+ recruitingPostId: number,
+ position?: PositionType,
+): Promise {
+ const { data } = await api.get>(
+ `my-page/${recruitingPostId}/current-applicants`,
+ {
+ params: {
+ position,
+ },
+ },
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch current applicants');
+ }
+ return data.result;
+}
+
+// ์ง์์ ํ๋กํ ์กฐํ
+export async function getApplicantProfile(
+ recruitingPostId: number,
+ applicationId: number,
+): Promise {
+ const { data } = await api.get>(
+ `my-page/${recruitingPostId}/${applicationId}`,
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch applicant profile');
+ }
+ return data.result;
+}
+
+// ์ง์์ ํฉ๋ถ ์ ํ๊ธฐ
+export async function updateApplicationStatus({
+ recruitingPostId,
+ applicationId,
+ applicationStatus,
+}: UpdateApplicationStatus) {
+ const { data } = await api.patch>(
+ `my-page/${recruitingPostId}/${applicationId}`,
+ {},
+ {
+ params: {
+ applicationStatus,
+ },
+ },
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to update application status');
+ }
+ return data.result;
+}
+
+// ํ์ ๋ชจ์ง ๋ง๊ฐ
+export async function closeRecruitingPost(recruitingPostId: number) {
+ const { data } = await api.patch>(`my-page/${recruitingPostId}`);
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to close recruiting post');
+ }
+ return data.result;
+}
diff --git a/src/libs/api/image.ts b/src/libs/api/image.ts
new file mode 100644
index 00000000..abf97c3e
--- /dev/null
+++ b/src/libs/api/image.ts
@@ -0,0 +1,52 @@
+import axios from 'axios';
+import api from './api';
+import { CommonResponse } from '@/types/common';
+import { PresignedUrlResponse } from '@/types/image';
+
+export const uploadToS3 = async (presignedUrl: string, file: File): Promise => {
+ try {
+ const response = await axios.put(presignedUrl, file, {
+ headers: {
+ 'Content-Type': file.type,
+ },
+ });
+ return response.status === 200;
+ } catch (error) {
+ console.error('Error uploading file to S3:', error);
+ return false;
+ }
+};
+
+export async function getPresignedUrl(imageName: string): Promise {
+ try {
+ const { data } = await api.post>(
+ `/preSigned-url?imageName=${encodeURIComponent(imageName)}`,
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to get presigned URL');
+ }
+
+ return data.result;
+ } catch (error) {
+ console.error('getPresignedUrl Error:', error);
+ throw error;
+ }
+}
+
+export const uploadImage = async (file: File): Promise => {
+ try {
+ const { preSignedUrl, objectKey } = await getPresignedUrl(file.name);
+
+ const success = await uploadToS3(preSignedUrl, file);
+
+ if (!success) {
+ throw new Error('S3 upload failed');
+ }
+
+ return objectKey;
+ } catch (error) {
+ console.error('uploadImage Error:', error);
+ return null;
+ }
+};
diff --git a/src/libs/api/mypage/applicant.ts b/src/libs/api/mypage/applicant.ts
new file mode 100644
index 00000000..d580d226
--- /dev/null
+++ b/src/libs/api/mypage/applicant.ts
@@ -0,0 +1,61 @@
+import { ResponseApplications, ResponseCurrentApplicants } from '@/types/applicant';
+import { CommonResponse } from '@/types/common';
+import api from '../api';
+import { MyTeamsResponse, ResponseConfirmedProfiles } from '@/types/myteam';
+
+export async function getMyApplications(
+ applicationStatus: string = '',
+ page: number = 0,
+ size: number = 6,
+): Promise {
+ const { data } = await api.get>('/my-page/applications', {
+ params: { applicationStatus, page, size },
+ });
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch applications');
+ }
+
+ return data.result;
+}
+
+export async function getCurrentApplicants(
+ recruitingStatus: string = '',
+ page: number = 0,
+ size: number = 6,
+): Promise {
+ const { data } = await api.get>(
+ '/my-page/current-applicants',
+ {
+ params: { recruitingStatus, page, size },
+ },
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch current applicants');
+ }
+
+ return data.result;
+}
+
+export const getConfirmedProfile = async (postId: number, position?: string) => {
+ const { data } = await api.get(`/confirmed-profile/${postId}`, {
+ params: {
+ position: position || undefined,
+ },
+ });
+
+ return data;
+};
+
+export async function getMyTeams(page: number = 0, size: number = 6): Promise {
+ const { data } = await api.get>('/my-page/my-teams', {
+ params: { page, size },
+ });
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch my teams');
+ }
+
+ return data.result;
+}
diff --git a/src/libs/api/mypage/dashboard.ts b/src/libs/api/mypage/dashboard.ts
new file mode 100644
index 00000000..5f13929c
--- /dev/null
+++ b/src/libs/api/mypage/dashboard.ts
@@ -0,0 +1,13 @@
+import { ResponseDashboard } from '@/types/project';
+import api from '../api';
+import { CommonResponse } from '@/types/common';
+
+export async function getDashboard(): Promise {
+ const { data } = await api.get>('/my-page/dashboard');
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch dashboard data');
+ }
+
+ return data.result;
+}
diff --git a/src/libs/api/profile.ts b/src/libs/api/profile.ts
new file mode 100644
index 00000000..6239bca0
--- /dev/null
+++ b/src/libs/api/profile.ts
@@ -0,0 +1,106 @@
+import { ResponseProfile } from '@/types/profile';
+import api from './api';
+import { CommonResponse } from '@/types/common';
+import { WORKING_VALUE_MAP } from '@/constants/Dropdown';
+
+export async function getSingleProfile(profileId: number): Promise {
+ const { data } = await api.get>(`profile/${profileId}`);
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch profile');
+ }
+ return data.result;
+}
+
+export async function getProfileList(): Promise {
+ const { data } = await api.get>('profile');
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch profile list');
+ }
+ return data.result;
+}
+
+export async function getUuidProfileList(userUuid: string): Promise {
+ const { data } = await api.get>(`${userUuid}/profile`);
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch uuid profile list');
+ }
+ return data.result;
+}
+
+export async function updateProfile(
+ profileId: number,
+ payload: {
+ profileName: string | null;
+ workingTime: string | null;
+ links: string[] | null;
+ contactWay: string | null;
+ },
+): Promise {
+ const convertedPayload = {
+ ...payload,
+ workingTime:
+ payload.workingTime === null
+ ? null
+ : (WORKING_VALUE_MAP[payload.workingTime] ?? payload.workingTime),
+ };
+
+ const { data } = await api.put>(
+ `profile/${profileId}`,
+ convertedPayload,
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to update profile');
+ }
+
+ return data.result;
+}
+
+export async function createProfile(payload: {
+ profileName: string | null;
+ workingTime: string | null;
+ links: string[] | null;
+ contactWay: string | null;
+}) {
+ const convertedPayload = {
+ ...payload,
+ workingTime:
+ payload.workingTime === null
+ ? null
+ : (WORKING_VALUE_MAP[payload.workingTime] ?? payload.workingTime),
+ };
+
+ const { data } = await api.post>(`/profile`, convertedPayload);
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to create profile');
+ }
+
+ return data.result;
+}
+
+export async function deleteProfile(profileId: number): Promise {
+ const { data } = await api.delete>(`profile/${profileId}`);
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to delete profile');
+ }
+ return data.result;
+}
+
+export const updateProfileImage = async (profileId: number, objectKey: string): Promise => {
+ const { data } = await api.put>(
+ `/profile/${profileId}/image?objectKey=${encodeURIComponent(objectKey)}`,
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to update profile image');
+ }
+};
+
+export const deleteProfileImage = async (profileId: number): Promise => {
+ const { data } = await api.delete>(`profile/${profileId}/image`);
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to delete profile image');
+ }
+};
diff --git a/src/libs/api/project.ts b/src/libs/api/project.ts
new file mode 100644
index 00000000..493109ee
--- /dev/null
+++ b/src/libs/api/project.ts
@@ -0,0 +1,73 @@
+import { CommonResponse } from '@/types/common';
+import {
+ CreateProject,
+ DeleteProject,
+ PostApplication,
+ Project,
+ ResponseApplication,
+ ResponseProject,
+ ResponseUpdateProject,
+} from '@/types/project';
+import api from './api';
+
+export async function getSingleProject(postId: number): Promise {
+ const { data } = await api.get>(`recruiting-posts/${postId}`);
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch project');
+ }
+ return data.result;
+}
+
+export async function postProject(project: CreateProject): Promise {
+ const { data } = await api.post>(`recruiting-posts`, {
+ ...project,
+ });
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to create project');
+ }
+ return data.result;
+}
+
+export async function deleteProject(postId: number): Promise {
+ const { data } = await api.delete>(`recruiting-posts/${postId}`);
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to delete project');
+ }
+ return data.result;
+}
+
+export async function postApplication(application: PostApplication): Promise {
+ try {
+ const { data } = await api.post>(`applications`, {
+ ...application,
+ });
+
+ if ('isSuccess' in data) {
+ const errorResponse = data as CommonResponse;
+ throw new Error(errorResponse.message || 'Failed to apply for project');
+ }
+
+ return data as ResponseApplication;
+ } catch (error) {
+ throw error;
+ }
+}
+
+export async function patchProject(
+ project: Project,
+ postId: number,
+): Promise {
+ const { data } = await api.patch>(
+ `recruiting-posts/${postId}`,
+ {
+ ...project,
+ },
+ );
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to update project');
+ }
+ return data.result;
+}
diff --git a/src/libs/api/recruitingPosts.ts b/src/libs/api/recruitingPosts.ts
new file mode 100644
index 00000000..4a4a1e50
--- /dev/null
+++ b/src/libs/api/recruitingPosts.ts
@@ -0,0 +1,26 @@
+import axios from 'axios';
+import { PagedProjects } from '@/types/project';
+import qs from 'qs';
+
+interface GetRecruitingPostsParams {
+ status?: string;
+ position?: string;
+ progressWay?: string;
+ page: number;
+ size: number;
+ sort?: string[];
+}
+
+export const getRecruitingPosts = async (params: GetRecruitingPostsParams) => {
+ console.log(params);
+ const response = await axios.get<{ result: PagedProjects }>(
+ `${process.env.NEXT_PUBLIC_API_BASE_URL}/recruiting-posts`,
+ {
+ params,
+ paramsSerializer: (params) =>
+ qs.stringify(params, { arrayFormat: 'indices', allowDots: true }),
+ },
+ );
+
+ return response.data.result;
+};
diff --git a/src/libs/api/teampsylog.ts b/src/libs/api/teampsylog.ts
new file mode 100644
index 00000000..ecaf9fec
--- /dev/null
+++ b/src/libs/api/teampsylog.ts
@@ -0,0 +1,156 @@
+import {
+ RequesterInfo,
+ RequestKeyword,
+ RequestKeywordComment,
+ RequestTeamficialLog,
+ ResponseKeyword,
+ ResponseKeywordComment,
+ ResponseKeywordList,
+ ResponseTeamficialLog,
+ RequestHeadKeyword,
+ ResponseHeadKeyword,
+ ResponseRandomKeywords,
+ RequestReportComment,
+} from '@/types/teampsylog';
+import api from './api';
+import { CommonResponse } from '@/types/common';
+
+export async function getKeywords(profileId: number): Promise {
+ const { data } = await api.get>(
+ `teamficial-log/head-keyword/${profileId}`,
+ );
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch keywords');
+ }
+ return data.result;
+}
+
+export async function getKeywordList({
+ userId,
+ page = 0,
+ size = 6,
+}: RequestKeyword): Promise {
+ const { data } = await api.get>(`teamficial-log/${userId}`, {
+ params: {
+ page,
+ size,
+ },
+ });
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch keyword list');
+ }
+ return data.result;
+}
+
+export async function postTeamficialLog(
+ body: RequestTeamficialLog,
+): Promise {
+ const { data } = await api.post>('/teamficial-log', body);
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to submit teamficial log');
+ }
+
+ return data.result;
+}
+
+export const getRequesterInfo = async (uuid: string): Promise => {
+ const { data } = await api.get>(`/teamficial-log/requester`, {
+ params: { requesterUuid: uuid },
+ });
+
+ if (!data.isSuccess) {
+ throw new Error(data.message);
+ }
+
+ return data.result;
+};
+
+export const getKeywordComments = async ({
+ keywordId,
+ page = 0,
+ size = 4,
+}: RequestKeywordComment): Promise => {
+ const { data } = await api.get>(
+ `teamficial-log/users/${keywordId}`,
+ {
+ params: {
+ page,
+ size,
+ },
+ },
+ );
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch keyword comments');
+ }
+
+ return data.result;
+};
+
+export const putHeadKeywords = async ({
+ profileId,
+ oldHeadKeywordId,
+ keywordId,
+}: RequestHeadKeyword): Promise => {
+ const params: { keywordId: number; oldHeadKeywordId?: number } = {
+ keywordId,
+ };
+
+ if (oldHeadKeywordId !== 0) {
+ params.oldHeadKeywordId = oldHeadKeywordId;
+ }
+
+ const { data } = await api.put>(
+ `/teamficial-log/head-keyword/${profileId}`,
+ null,
+ { params },
+ );
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to update head keywords');
+ }
+
+ return data.result;
+};
+
+export const getRandomKeywords = async (requesterUuid: string): Promise => {
+ const { data } = await api.get>(`/teamficial-log/rand`, {
+ params: {
+ requesterUuid,
+ },
+ });
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to fetch random keywords');
+ }
+
+ return data.result;
+};
+
+export async function postReportComment({
+ keywordCommentId,
+ reportType,
+ reportEtc,
+ content,
+}: RequestReportComment): Promise> {
+ // body์์ keywordCommentId ์ ์ธ
+ const body: {
+ reportType: string;
+ reportEtc?: string;
+ content: string;
+ } = {
+ reportType,
+ content,
+ };
+
+ // reportEtc๊ฐ ์์ ๋๋ง ํฌํจ
+ if (reportEtc) {
+ body.reportEtc = reportEtc;
+ }
+
+ const { data } = await api.post>(`reports/${keywordCommentId}`, body);
+
+ if (!data.isSuccess) {
+ throw new Error(data.message || 'Failed to report keyword comment');
+ }
+ return data;
+}
diff --git a/src/libs/schemas/projectSchema.ts b/src/libs/schemas/projectSchema.ts
new file mode 100644
index 00000000..bf834a48
--- /dev/null
+++ b/src/libs/schemas/projectSchema.ts
@@ -0,0 +1,75 @@
+import { POSITION_VALUES } from '@/utils/position';
+import { Period, PeriodType, ProgressWay } from '@/utils/project';
+import { z } from 'zod';
+
+const extractTextFromHTML = (html: string): string => {
+ const text = html
+ .replace(/
/gi, '\n')
+ .replace(/<\/(div|p|li|h[1-6])>/gi, '\n')
+ .replace(/<[^>]*>/g, '')
+ .replace(/\r?\n/g, '')
+ .trim();
+ return text;
+};
+
+export const recruitFormSchema = z.object({
+ title: z.string().min(1, '์ ๋ชฉ์ ์
๋ ฅํด์ฃผ์ธ์').max(30),
+ recruitingPositions: z
+ .array(
+ z.object({
+ position: z.enum(POSITION_VALUES, {
+ message: '๋ชจ์ง๋ถ์ผ๋ฅผ ์ ํํด์ฃผ์ธ์',
+ }),
+ count: z.number().min(1, '์ธ์ ์๋ฅผ ์
๋ ฅํด์ฃผ์ธ์'),
+ }),
+ )
+ .min(1, '์ต์ 1๊ฐ ์ด์์ ๋ชจ์ง ๋ถ์ผ๊ฐ ํ์ํฉ๋๋ค'),
+ progressWay: z.enum(ProgressWay, {
+ message: '์งํ ๋ฐฉ๋ฒ์ ์ ํํด์ฃผ์ธ์',
+ }),
+ startDate: z
+ .string({
+ message: '์์์ผ์ ์ ํํด์ฃผ์ธ์',
+ })
+ .min(1, '์์์ผ์ ์ ํํด์ฃผ์ธ์'),
+ period: z.enum(Object.values(Period) as [PeriodType, ...PeriodType[]], {
+ message: 'ํ๋ก์ ํธ ๊ธฐ๊ฐ์ ์ ํํด์ฃผ์ธ์',
+ }),
+ deadline: z
+ .string({
+ message: '๋ง๊ฐ์ผ์ ์ ํํด์ฃผ์ธ์',
+ })
+ .min(1, '๋ง๊ฐ์ผ์ ์ ํํด์ฃผ์ธ์'),
+ contactWay: z.string().min(1, '์ฐ๋ฝ ๋ฐฉ๋ฒ์ ์
๋ ฅํด์ฃผ์ธ์'),
+ content: z.string().refine(
+ (html) => {
+ const text = extractTextFromHTML(html);
+ return text.length >= 50;
+ },
+ { message: '์ต์ 50์ ์ด์ ์์ฑํด์ฃผ์ธ์.' },
+ ),
+ profileId: z.number().min(1).optional(),
+});
+
+export type RecruitFormType = z.infer;
+
+// create ๋ชจ๋ ์ ์ฉ ์คํค๋ง (profileId ํ์)
+export const createRecruitFormSchema = recruitFormSchema.required({
+ profileId: true,
+});
+export type CreateRecruitFormType = z.infer;
+
+// profileId ์ ์ธ ์คํค๋ง (1๋จ๊ณ ํผ ๊ฒ์ฆ์ฉ)
+export const recruitFormWithoutProfileSchema = recruitFormSchema.omit({ profileId: true });
+export type RecruitFormWithoutProfileType = z.infer;
+
+// ํ๋กํ ์ ํ ์ ์ฉ ์คํค๋ง (2๋จ๊ณ ํ๋กํ ์ ํ์ฉ)
+export const profileSelectSchema = z.object({
+ profileId: z
+ .number({
+ error: 'ํ๋กํ์ ์ ํํด์ฃผ์ธ์',
+ })
+ .min(1, 'ํ๋กํ์ ์ ํํด์ฃผ์ธ์'),
+});
+
+export type ProfileSelectType = z.infer;
diff --git a/src/libs/schemas/questionFormSchema.ts b/src/libs/schemas/questionFormSchema.ts
new file mode 100644
index 00000000..714510a2
--- /dev/null
+++ b/src/libs/schemas/questionFormSchema.ts
@@ -0,0 +1,14 @@
+import { z } from 'zod';
+
+const questionSetSchema = z.object({
+ question: z.string().min(1, '์ง๋ฌธ์ ์ ํํด์ฃผ์ธ์.'),
+ answer: z.string().min(1, '๋ต๋ณ์ ์
๋ ฅํด์ฃผ์ธ์.').max(250, '์ต๋ 250์๊น์ง ๊ฐ๋ฅํฉ๋๋ค.'),
+});
+
+export const questionFormSchema = z.object({
+ set1: questionSetSchema,
+ set2: questionSetSchema,
+ set3: questionSetSchema,
+});
+
+export type QuestionFormValues = z.infer;
diff --git a/src/store/useUserStore.ts b/src/store/useUserStore.ts
new file mode 100644
index 00000000..453b698a
--- /dev/null
+++ b/src/store/useUserStore.ts
@@ -0,0 +1,38 @@
+import { create } from 'zustand';
+import { persist } from 'zustand/middleware';
+
+interface UserState {
+ uuid: string | null;
+ userId: string | null;
+ userName: string | null;
+ _hasHydrated: boolean;
+ setUser: (data: { uuid: string; userId: string; userName: string }) => void;
+ clearUser: () => void;
+ setHasHydrated: (value: boolean) => void;
+}
+
+export const useUserStore = create()(
+ persist(
+ (set) => ({
+ uuid: null,
+ userId: null,
+ userName: null,
+ _hasHydrated: false,
+ setUser: (data) => set(data),
+ clearUser: () => {
+ set({ uuid: null, userId: null, userName: null });
+ localStorage.removeItem('accessToken');
+ localStorage.removeItem('refreshToken');
+ },
+ setHasHydrated: (value) => set({ _hasHydrated: value }),
+ }),
+ {
+ name: 'user',
+ onRehydrateStorage: () => (state) => {
+ if (state) {
+ state.setHasHydrated(true);
+ }
+ },
+ },
+ ),
+);
diff --git a/src/styles/_keyframe-animations.scss b/src/styles/_keyframe-animations.scss
new file mode 100644
index 00000000..dd98b7cb
--- /dev/null
+++ b/src/styles/_keyframe-animations.scss
@@ -0,0 +1,91 @@
+@keyframes fadeIn {
+ from {
+ opacity: 0;
+ }
+ to {
+ opacity: 1;
+ }
+}
+
+@keyframes fadeOut {
+ from {
+ opacity: 1;
+ }
+ to {
+ opacity: 0;
+ }
+}
+
+@keyframes zoomIn {
+ from {
+ transform: scale(0.95);
+ }
+ to {
+ transform: scale(1);
+ }
+}
+
+@keyframes zoomOut {
+ from {
+ transform: scale(1);
+ }
+ to {
+ transform: scale(0.95);
+ }
+}
+
+@keyframes zoom {
+ 0% {
+ opacity: 0;
+ transform: scale(0.95);
+ }
+ 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+}
+
+@keyframes slideFromTop {
+ from {
+ transform: translateY(-0.5rem);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
+
+@keyframes slideFromRight {
+ from {
+ transform: translateX(0.5rem);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideFromLeft {
+ from {
+ transform: translateX(-0.5rem);
+ }
+ to {
+ transform: translateX(0);
+ }
+}
+
+@keyframes slideFromBottom {
+ from {
+ transform: translateY(0.5rem);
+ }
+ to {
+ transform: translateY(0);
+ }
+}
+
+@keyframes spin {
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
+}
diff --git a/src/styles/_variables.scss b/src/styles/_variables.scss
new file mode 100644
index 00000000..113a16b4
--- /dev/null
+++ b/src/styles/_variables.scss
@@ -0,0 +1,296 @@
+:root {
+ /******************
+ Basics
+ ******************/
+
+ overflow-wrap: break-word;
+ text-size-adjust: none;
+ text-rendering: optimizeLegibility;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+
+ /******************
+ Colors variables
+ ******************/
+
+ /* Gray alpha (light mode) */
+ --tt-gray-light-a-50: rgba(56, 56, 56, 0.04);
+ --tt-gray-light-a-100: rgba(15, 22, 36, 0.05);
+ --tt-gray-light-a-200: rgba(37, 39, 45, 0.1);
+ --tt-gray-light-a-300: rgba(47, 50, 55, 0.2);
+ --tt-gray-light-a-400: rgba(40, 44, 51, 0.42);
+ --tt-gray-light-a-500: rgba(52, 55, 60, 0.64);
+ --tt-gray-light-a-600: rgba(36, 39, 46, 0.78);
+ --tt-gray-light-a-700: rgba(35, 37, 42, 0.87);
+ --tt-gray-light-a-800: rgba(30, 32, 36, 0.95);
+ --tt-gray-light-a-900: rgba(29, 30, 32, 0.98);
+
+ /* Gray (light mode) */
+ --tt-gray-light-50: rgba(250, 250, 250, 1);
+ --tt-gray-light-100: rgba(244, 244, 245, 1);
+ --tt-gray-light-200: rgba(234, 234, 235, 1);
+ --tt-gray-light-300: rgba(213, 214, 215, 1);
+ --tt-gray-light-400: rgba(166, 167, 171, 1);
+ --tt-gray-light-500: rgba(125, 127, 130, 1);
+ --tt-gray-light-600: rgba(83, 86, 90, 1);
+ --tt-gray-light-700: rgba(64, 65, 69, 1);
+ --tt-gray-light-800: rgba(44, 45, 48, 1);
+ --tt-gray-light-900: rgba(34, 35, 37, 1);
+
+ /* Gray alpha (dark mode) */
+ --tt-gray-dark-a-50: rgba(232, 232, 253, 0.05);
+ --tt-gray-dark-a-100: rgba(231, 231, 243, 0.07);
+ --tt-gray-dark-a-200: rgba(238, 238, 246, 0.11);
+ --tt-gray-dark-a-300: rgba(239, 239, 245, 0.22);
+ --tt-gray-dark-a-400: rgba(244, 244, 255, 0.37);
+ --tt-gray-dark-a-500: rgba(236, 238, 253, 0.5);
+ --tt-gray-dark-a-600: rgba(247, 247, 253, 0.64);
+ --tt-gray-dark-a-700: rgba(251, 251, 254, 0.75);
+ --tt-gray-dark-a-800: rgba(253, 253, 253, 0.88);
+ --tt-gray-dark-a-900: rgba(255, 255, 255, 0.96);
+
+ /* Gray (dark mode) */
+ --tt-gray-dark-50: rgba(25, 25, 26, 1);
+ --tt-gray-dark-100: rgba(32, 32, 34, 1);
+ --tt-gray-dark-200: rgba(45, 45, 47, 1);
+ --tt-gray-dark-300: rgba(70, 70, 73, 1);
+ --tt-gray-dark-400: rgba(99, 99, 105, 1);
+ --tt-gray-dark-500: rgba(124, 124, 131, 1);
+ --tt-gray-dark-600: rgba(163, 163, 168, 1);
+ --tt-gray-dark-700: rgba(192, 192, 195, 1);
+ --tt-gray-dark-800: rgba(224, 224, 225, 1);
+ --tt-gray-dark-900: rgba(245, 245, 245, 1);
+
+ /* Brand colors */
+ --tt-brand-color-50: rgba(239, 238, 255, 1);
+ --tt-brand-color-100: rgba(222, 219, 255, 1);
+ --tt-brand-color-200: rgba(195, 189, 255, 1);
+ --tt-brand-color-300: rgba(157, 138, 255, 1);
+ --tt-brand-color-400: rgba(122, 82, 255, 1);
+ --tt-brand-color-500: rgba(98, 41, 255, 1);
+ --tt-brand-color-600: rgba(84, 0, 229, 1);
+ --tt-brand-color-700: rgba(75, 0, 204, 1);
+ --tt-brand-color-800: rgba(56, 0, 153, 1);
+ --tt-brand-color-900: rgba(43, 25, 102, 1);
+ --tt-brand-color-950: hsla(257, 100%, 9%, 1);
+
+ /* Green */
+ --tt-color-green-inc-5: hsla(129, 100%, 97%, 1);
+ --tt-color-green-inc-4: hsla(129, 100%, 92%, 1);
+ --tt-color-green-inc-3: hsla(131, 100%, 86%, 1);
+ --tt-color-green-inc-2: hsla(133, 98%, 78%, 1);
+ --tt-color-green-inc-1: hsla(137, 99%, 70%, 1);
+ --tt-color-green-base: hsla(147, 99%, 50%, 1);
+ --tt-color-green-dec-1: hsla(147, 97%, 41%, 1);
+ --tt-color-green-dec-2: hsla(146, 98%, 32%, 1);
+ --tt-color-green-dec-3: hsla(146, 100%, 24%, 1);
+ --tt-color-green-dec-4: hsla(144, 100%, 16%, 1);
+ --tt-color-green-dec-5: hsla(140, 100%, 9%, 1);
+
+ /* Yellow */
+ --tt-color-yellow-inc-5: hsla(50, 100%, 97%, 1);
+ --tt-color-yellow-inc-4: hsla(50, 100%, 91%, 1);
+ --tt-color-yellow-inc-3: hsla(50, 100%, 84%, 1);
+ --tt-color-yellow-inc-2: hsla(50, 100%, 77%, 1);
+ --tt-color-yellow-inc-1: hsla(50, 100%, 68%, 1);
+ --tt-color-yellow-base: hsla(52, 100%, 50%, 1);
+ --tt-color-yellow-dec-1: hsla(52, 100%, 41%, 1);
+ --tt-color-yellow-dec-2: hsla(52, 100%, 32%, 1);
+ --tt-color-yellow-dec-3: hsla(52, 100%, 24%, 1);
+ --tt-color-yellow-dec-4: hsla(51, 100%, 16%, 1);
+ --tt-color-yellow-dec-5: hsla(50, 100%, 9%, 1);
+
+ /* Red */
+ --tt-color-red-inc-5: hsla(11, 100%, 96%, 1);
+ --tt-color-red-inc-4: hsla(11, 100%, 88%, 1);
+ --tt-color-red-inc-3: hsla(10, 100%, 80%, 1);
+ --tt-color-red-inc-2: hsla(9, 100%, 73%, 1);
+ --tt-color-red-inc-1: hsla(7, 100%, 64%, 1);
+ --tt-color-red-base: hsla(7, 100%, 54%, 1);
+ --tt-color-red-dec-1: hsla(7, 100%, 41%, 1);
+ --tt-color-red-dec-2: hsla(5, 100%, 32%, 1);
+ --tt-color-red-dec-3: hsla(4, 100%, 24%, 1);
+ --tt-color-red-dec-4: hsla(3, 100%, 16%, 1);
+ --tt-color-red-dec-5: hsla(1, 100%, 9%, 1);
+
+ /* Basic colors */
+ --white: rgba(255, 255, 255, 1);
+ --black: rgba(14, 14, 17, 1);
+ --transparent: rgba(255, 255, 255, 0);
+
+ /******************
+ Shadow variables
+ ******************/
+
+ /* Shadows Light */
+ --tt-shadow-elevated-md:
+ 0px 16px 48px 0px rgba(17, 24, 39, 0.04),
+ 0px 12px 24px 0px rgba(17, 24, 39, 0.04),
+ 0px 6px 8px 0px rgba(17, 24, 39, 0.02),
+ 0px 2px 3px 0px rgba(17, 24, 39, 0.02);
+
+ /**************************************************
+ Radius variables
+ **************************************************/
+
+ --tt-radius-xxs: 0.125rem; /* 2px */
+ --tt-radius-xs: 0.25rem; /* 4px */
+ --tt-radius-sm: 0.375rem; /* 6px */
+ --tt-radius-md: 0.5rem; /* 8px */
+ --tt-radius-lg: 0.75rem; /* 12px */
+ --tt-radius-xl: 1rem; /* 16px */
+
+ /**************************************************
+ Transition variables
+ **************************************************/
+
+ --tt-transition-duration-short: 0.1s;
+ --tt-transition-duration-default: 0.2s;
+ --tt-transition-duration-long: 0.64s;
+ --tt-transition-easing-default: cubic-bezier(0.46, 0.03, 0.52, 0.96);
+ --tt-transition-easing-cubic: cubic-bezier(0.65, 0.05, 0.36, 1);
+ --tt-transition-easing-quart: cubic-bezier(0.77, 0, 0.18, 1);
+ --tt-transition-easing-circ: cubic-bezier(0.79, 0.14, 0.15, 0.86);
+ --tt-transition-easing-back: cubic-bezier(0.68, -0.55, 0.27, 1.55);
+
+ /******************
+ Contrast variables
+ ******************/
+
+ --tt-accent-contrast: 8%;
+ --tt-destructive-contrast: 8%;
+ --tt-foreground-contrast: 8%;
+
+ &,
+ *,
+ ::before,
+ ::after {
+ box-sizing: border-box;
+ transition: none var(--tt-transition-duration-default)
+ var(--tt-transition-easing-default);
+ }
+}
+
+:root {
+ /**************************************************
+ Global colors
+ **************************************************/
+
+ /* Global colors - Light mode */
+ --tt-bg-color: var(--white);
+ --tt-border-color: var(--tt-gray-light-a-200);
+ --tt-border-color-tint: var(--tt-gray-light-a-100);
+ --tt-sidebar-bg-color: var(--tt-gray-light-100);
+ --tt-scrollbar-color: var(--tt-gray-light-a-200);
+ --tt-cursor-color: var(--tt-brand-color-500);
+ --tt-selection-color: rgba(157, 138, 255, 0.2);
+ --tt-card-bg-color: var(--white);
+ --tt-card-border-color: var(--tt-gray-light-a-100);
+}
+
+/* Global colors - Dark mode */
+.dark {
+ --tt-bg-color: var(--black);
+ --tt-border-color: var(--tt-gray-dark-a-200);
+ --tt-border-color-tint: var(--tt-gray-dark-a-100);
+ --tt-sidebar-bg-color: var(--tt-gray-dark-100);
+ --tt-scrollbar-color: var(--tt-gray-dark-a-200);
+ --tt-cursor-color: var(--tt-brand-color-400);
+ --tt-selection-color: rgba(122, 82, 255, 0.2);
+ --tt-card-bg-color: var(--tt-gray-dark-50);
+ --tt-card-border-color: var(--tt-gray-dark-a-50);
+
+ --tt-shadow-elevated-md:
+ 0px 16px 48px 0px rgba(0, 0, 0, 0.5), 0px 12px 24px 0px rgba(0, 0, 0, 0.24),
+ 0px 6px 8px 0px rgba(0, 0, 0, 0.22), 0px 2px 3px 0px rgba(0, 0, 0, 0.12);
+}
+
+/* Text colors */
+:root {
+ --tt-color-text-gray: hsl(45, 2%, 46%);
+ --tt-color-text-brown: hsl(19, 31%, 47%);
+ --tt-color-text-orange: hsl(30, 89%, 45%);
+ --tt-color-text-yellow: hsl(38, 62%, 49%);
+ --tt-color-text-green: hsl(148, 32%, 39%);
+ --tt-color-text-blue: hsl(202, 54%, 43%);
+ --tt-color-text-purple: hsl(274, 32%, 54%);
+ --tt-color-text-pink: hsl(328, 49%, 53%);
+ --tt-color-text-red: hsl(2, 62%, 55%);
+
+ --tt-color-text-gray-contrast: hsla(39, 26%, 26%, 0.15);
+ --tt-color-text-brown-contrast: hsla(18, 43%, 69%, 0.35);
+ --tt-color-text-orange-contrast: hsla(24, 73%, 55%, 0.27);
+ --tt-color-text-yellow-contrast: hsla(44, 82%, 59%, 0.39);
+ --tt-color-text-green-contrast: hsla(126, 29%, 60%, 0.27);
+ --tt-color-text-blue-contrast: hsla(202, 54%, 59%, 0.27);
+ --tt-color-text-purple-contrast: hsla(274, 37%, 64%, 0.27);
+ --tt-color-text-pink-contrast: hsla(331, 60%, 71%, 0.27);
+ --tt-color-text-red-contrast: hsla(8, 79%, 79%, 0.4);
+}
+
+.dark {
+ --tt-color-text-gray: hsl(0, 0%, 61%);
+ --tt-color-text-brown: hsl(18, 35%, 58%);
+ --tt-color-text-orange: hsl(25, 53%, 53%);
+ --tt-color-text-yellow: hsl(36, 54%, 55%);
+ --tt-color-text-green: hsl(145, 32%, 47%);
+ --tt-color-text-blue: hsl(202, 64%, 52%);
+ --tt-color-text-purple: hsl(270, 55%, 62%);
+ --tt-color-text-pink: hsl(329, 57%, 58%);
+ --tt-color-text-red: hsl(1, 69%, 60%);
+
+ --tt-color-text-gray-contrast: hsla(0, 0%, 100%, 0.09);
+ --tt-color-text-brown-contrast: hsla(17, 45%, 50%, 0.25);
+ --tt-color-text-orange-contrast: hsla(27, 82%, 53%, 0.2);
+ --tt-color-text-yellow-contrast: hsla(35, 49%, 47%, 0.2);
+ --tt-color-text-green-contrast: hsla(151, 55%, 39%, 0.2);
+ --tt-color-text-blue-contrast: hsla(202, 54%, 43%, 0.2);
+ --tt-color-text-purple-contrast: hsla(271, 56%, 60%, 0.18);
+ --tt-color-text-pink-contrast: hsla(331, 67%, 58%, 0.22);
+ --tt-color-text-red-contrast: hsla(0, 67%, 60%, 0.25);
+}
+
+/* Highlight colors */
+:root {
+ --tt-color-highlight-yellow: #fef9c3;
+ --tt-color-highlight-green: #dcfce7;
+ --tt-color-highlight-blue: #e0f2fe;
+ --tt-color-highlight-purple: #f3e8ff;
+ --tt-color-highlight-red: #ffe4e6;
+ --tt-color-highlight-gray: rgb(248, 248, 247);
+ --tt-color-highlight-brown: rgb(244, 238, 238);
+ --tt-color-highlight-orange: rgb(251, 236, 221);
+ --tt-color-highlight-pink: rgb(252, 241, 246);
+
+ --tt-color-highlight-yellow-contrast: #fbe604;
+ --tt-color-highlight-green-contrast: #c7fad8;
+ --tt-color-highlight-blue-contrast: #ceeafd;
+ --tt-color-highlight-purple-contrast: #e4ccff;
+ --tt-color-highlight-red-contrast: #ffccd0;
+ --tt-color-highlight-gray-contrast: rgba(84, 72, 49, 0.15);
+ --tt-color-highlight-brown-contrast: rgba(210, 162, 141, 0.35);
+ --tt-color-highlight-orange-contrast: rgba(224, 124, 57, 0.27);
+ --tt-color-highlight-pink-contrast: rgba(225, 136, 179, 0.27);
+}
+
+.dark {
+ --tt-color-highlight-yellow: #6b6524;
+ --tt-color-highlight-green: #509568;
+ --tt-color-highlight-blue: #6e92aa;
+ --tt-color-highlight-purple: #583e74;
+ --tt-color-highlight-red: #743e42;
+ --tt-color-highlight-gray: rgb(47, 47, 47);
+ --tt-color-highlight-brown: rgb(74, 50, 40);
+ --tt-color-highlight-orange: rgb(92, 59, 35);
+ --tt-color-highlight-pink: rgb(78, 44, 60);
+
+ --tt-color-highlight-yellow-contrast: #58531e;
+ --tt-color-highlight-green-contrast: #47855d;
+ --tt-color-highlight-blue-contrast: #5e86a1;
+ --tt-color-highlight-purple-contrast: #4c3564;
+ --tt-color-highlight-red-contrast: #643539;
+ --tt-color-highlight-gray-contrast: rgba(255, 255, 255, 0.094);
+ --tt-color-highlight-brown-contrast: rgba(184, 101, 69, 0.25);
+ --tt-color-highlight-orange-contrast: rgba(233, 126, 37, 0.2);
+ --tt-color-highlight-pink-contrast: rgba(220, 76, 145, 0.22);
+}
diff --git a/src/types/applicant.ts b/src/types/applicant.ts
new file mode 100644
index 00000000..b1e1a84f
--- /dev/null
+++ b/src/types/applicant.ts
@@ -0,0 +1,41 @@
+export interface Application {
+ recruitingPostId: number;
+ writerName: string;
+ profileImage: string | null;
+ title: string;
+ tags: string[];
+ progressWay: string;
+ status: string;
+ period: string;
+ deadline: string;
+ createdAt: string;
+}
+
+export interface ResponseApplications {
+ content: Application[];
+ currentPage: number;
+ totalPages: number;
+ totalElements: number;
+ hasNext: boolean;
+}
+
+export interface CurrentApplicant {
+ recruitingPostId: number;
+ recruitingPostStatus: string;
+ writerName: string;
+ profileImage: string | null;
+ title: string;
+ tags: string[];
+ deadline: string;
+ totalApplicants: number;
+ createdAt: string;
+ dday: number;
+}
+
+export interface ResponseCurrentApplicants {
+ content: CurrentApplicant[];
+ currentPage: number;
+ totalPages: number;
+ totalElements: number;
+ hasNext: boolean;
+}
diff --git a/src/types/application.ts b/src/types/application.ts
new file mode 100644
index 00000000..2a0df45d
--- /dev/null
+++ b/src/types/application.ts
@@ -0,0 +1,61 @@
+import { ApplicationStatusType } from '@/utils/applicate';
+
+// ๋ชจ์ง ๊ธ ์ง์์ ์กฐํ
+export type CurrentRecruitingPost = {
+ recruitingPostId: number;
+ recruitingPostTitle: string;
+ recruitingPostContent: string;
+ progressWay: string;
+ contactWay: string;
+ startDate: string;
+ period: string;
+ deadline: string;
+ recruitingDetails: string[];
+ createdAt: string;
+ dday: number;
+ status: string;
+};
+
+export type CurrentApplicant = {
+ applicationId: number;
+ profileId: number;
+ applicantName: string;
+ profilePosition: string;
+ profileImage: string;
+ applicationStatus: string;
+ keywordList?: string[];
+};
+
+export type ResponseApplicantsDetail = {
+ recruitingPost: CurrentRecruitingPost;
+ applicantList: CurrentApplicant[];
+};
+
+// ์ง์์ ํ๋กํ ์กฐํ
+export type ResponseProfile = {
+ profileId: number;
+ userId: number;
+ userName: string;
+ profileImageUrl: string;
+ profileName: string;
+ workingTime: string;
+ links: string[];
+ contactWay: string;
+ createdAt: string;
+ modifiedAt: string;
+ headKeywords: string[];
+ uuid: string;
+};
+
+export type ApplicationResponse = {
+ applicationId: number;
+ content: string;
+ profile: ResponseProfile;
+};
+
+// ์ง์์ ํฉ๋ถ ์ ํ๊ธฐ
+export type UpdateApplicationStatus = {
+ recruitingPostId: number;
+ applicationId: number;
+ applicationStatus: ApplicationStatusType;
+};
diff --git a/src/types/common.ts b/src/types/common.ts
new file mode 100644
index 00000000..5cddc8b5
--- /dev/null
+++ b/src/types/common.ts
@@ -0,0 +1,6 @@
+export type CommonResponse = {
+ isSuccess: boolean;
+ code: string;
+ message: string;
+ result: T;
+};
diff --git a/src/types/image.ts b/src/types/image.ts
new file mode 100644
index 00000000..a07fbb3b
--- /dev/null
+++ b/src/types/image.ts
@@ -0,0 +1,5 @@
+export interface PresignedUrlResponse {
+ preSignedUrl: string;
+ objectKey: string;
+ expiresAt: string;
+}
diff --git a/src/types/myteam.ts b/src/types/myteam.ts
new file mode 100644
index 00000000..4c0fb3e0
--- /dev/null
+++ b/src/types/myteam.ts
@@ -0,0 +1,31 @@
+export interface ResponseConfirmedProfile {
+ userName: string;
+ profileName: string;
+ profileImage: string;
+ workingTime?: string;
+ contactWay?: string;
+ position: string;
+ keywords: string[];
+ links: string[];
+ uuid: string;
+}
+
+export type ResponseConfirmedProfiles = ResponseConfirmedProfile[];
+
+export interface MyTeamItem {
+ postId: number;
+ period: string;
+ title: string;
+ progressWay: string;
+ tags: string[];
+ totalMembers: number;
+ createAt: string;
+}
+
+export interface MyTeamsResponse {
+ content: MyTeamItem[];
+ currentPage: number;
+ totalPages: number;
+ totalElements: number;
+ hasNext: boolean;
+}
diff --git a/src/types/profile.ts b/src/types/profile.ts
new file mode 100644
index 00000000..61f2e5f8
--- /dev/null
+++ b/src/types/profile.ts
@@ -0,0 +1,90 @@
+import { PositionType } from '@/utils/position';
+import { TimeType } from '@/utils/workingTime';
+
+// /profile ๊ด๋ จ API
+export type CreateProfile = {
+ profileName: string;
+ position: PositionType;
+ workingTime: TimeType;
+ links: string[];
+ contactWay: string;
+};
+
+export type ResponseProfile = {
+ profileId: number;
+ userId: number;
+ userName: string;
+ profileImageUrl: string;
+ profileName: string;
+ position: string;
+ workingTime: string;
+ links: string[];
+ contactWay: string;
+ headKeywords: string[];
+ createdAt: string;
+ modifiedAt: string;
+ uuid: string;
+};
+
+// /my-page ๊ด๋ จ API
+export type ApplicantProfile = {
+ applicationId: number;
+ content: string;
+ profile: ResponseProfile;
+};
+
+export type RecruitingPost = {
+ recruitingPostId: number;
+ recruitingPostTitle: string;
+ recruitingPostContent: string;
+ progressWay: string;
+ contactWay: string;
+ startDate: string;
+ period: string;
+ deadline: string;
+ recruitingDetails: string[];
+};
+
+export type ApplicantList = {
+ applicationId: number;
+ profileId: number;
+ applicantName: string;
+ profilePosition: string;
+ profileImage: string;
+};
+
+export type ApplicationStatus = {
+ recruitingPost: RecruitingPost;
+ applicantList: ApplicantList[];
+};
+
+export type CurrentApplicantItem = {
+ recruitingPostId: number;
+ writerName: string;
+ profileImage: string;
+ title: string;
+ tags: string[];
+ deadline: string;
+ totalApplicants: number;
+ createdAt: string;
+};
+
+export type MyApplicants = {
+ recruitingPostId: number;
+ writerName: string;
+ profileImage: string;
+ title: string;
+ tags: string[];
+ status: string;
+ period: string;
+ deadline: string;
+ createdAt: string;
+};
+
+export type PagedResponse = {
+ content: T[];
+ currentPage: number;
+ totalPages: number;
+ totalElements: number;
+ hasNext: boolean;
+};
diff --git a/src/types/project.ts b/src/types/project.ts
new file mode 100644
index 00000000..713a5f1c
--- /dev/null
+++ b/src/types/project.ts
@@ -0,0 +1,134 @@
+import { PositionType } from '@/utils/position';
+import { PeriodType, ProgressWayType, StatusType } from '@/utils/project';
+
+export type RecruitingPosition = {
+ position: PositionType;
+ count: number;
+};
+
+export type Project = {
+ progressWay: ProgressWayType;
+ contactWay: string;
+ startDate: string;
+ period: PeriodType;
+ deadline: string;
+ status: StatusType;
+ content: string;
+ title: string;
+ recruitingPositions: RecruitingPosition[];
+};
+
+export type CreateProject = Project & {
+ profileId: number;
+};
+
+export type ResponseProject = Project & {
+ postId: number;
+ writerUserId: number;
+ writerProfileId: number;
+ userName: string;
+ createdAt: string;
+ dday: number;
+ alreadyApplied: boolean;
+ writer: boolean;
+ profileImageUrl: string;
+};
+
+export type DeleteProject = {
+ deletedPostId: number;
+ message: string;
+};
+
+export type ResponseUpdateProject = Project & {
+ createdAt: string;
+};
+
+// ๋ชจ์ง ๊ธ ์ ์ฒด ์กฐํ
+export type Sort = {
+ unsorted: boolean;
+ sorted: boolean;
+ empty: boolean;
+};
+
+export type Pageable = {
+ pageNumber: number;
+ pageSize: number;
+ sort: Sort;
+ offset: number;
+ paged: boolean;
+ unpaged: boolean;
+};
+
+export type PagedProjects = {
+ totalElements: number;
+ totalPages: number;
+ pageable: Pageable;
+ numberOfElements: number;
+ size: number;
+ content: ResponseProject[];
+ number: number;
+ sort: Sort;
+ first: boolean;
+ last: boolean;
+ empty: boolean;
+};
+
+// ๋ชจ์ง ๊ธ ์ง์ํ๊ธฐ
+export type PostApplication = {
+ profileId: number;
+ recruitingPostId: number;
+ position: PositionType;
+ content?: string;
+};
+
+export type ResponseApplication = {
+ applicationId: number;
+ status: StatusType;
+ position: PositionType;
+ userId: number;
+ profileId: number;
+ recruitingPostId: number;
+ message: string;
+};
+
+// ๋ง์ดํ์ด์ง dashboard Response
+export type MyApplication = {
+ recruitingPostId: number;
+ writerName: string;
+ progressWay: string;
+ profileImage: string;
+ title: string;
+ tags: string[];
+ status: string;
+ period: string;
+ deadline: string;
+ createdAt: string;
+};
+
+export type MyRecruitingPost = {
+ recruitingPostId: number;
+ recruitingPostStatus: string;
+ writerName: string;
+ profileImage: string;
+ title: string;
+ tags: string[];
+ deadline: string;
+ totalApplicants: number;
+ createdAt: string;
+ dday: number;
+};
+
+export type MyTeamResponses = {
+ postId: number;
+ period: string;
+ title: string;
+ progressWay: string;
+ tags: string[];
+ totalMembers: number;
+};
+
+export type ResponseDashboard = {
+ myApplications: MyApplication[];
+ myRecruitingPost: MyRecruitingPost[];
+ myTeamResponses: MyTeamResponses[];
+};
diff --git a/src/types/teampsylog.ts b/src/types/teampsylog.ts
new file mode 100644
index 00000000..666e3d76
--- /dev/null
+++ b/src/types/teampsylog.ts
@@ -0,0 +1,88 @@
+export type ResponseKeyword = {
+ profileId: number;
+ profileName: string;
+ headKeywords: {
+ headKeywordId: number;
+ headKeywordName: string;
+ }[];
+};
+
+export type RequestKeyword = {
+ userId: number;
+ page?: number;
+ size?: number;
+};
+
+export type ResponseKeywordList = {
+ content: {
+ keywordId: number;
+ keywordName: string;
+ count: number;
+ head: boolean;
+ }[];
+ currentPage: number;
+ totalPages: number;
+ totalElements: number;
+ hasNext: boolean;
+};
+
+export interface RequestTeamficialLog {
+ userUuid: string;
+ content1: string;
+ content2: string;
+ content3: string;
+}
+
+export interface ResponseTeamficialLog {
+ contentPairs: {
+ keyword: string;
+ content: string;
+ }[];
+}
+
+export interface RequesterInfo {
+ userId: number;
+ requesterName: string;
+}
+
+export interface RequestKeywordComment {
+ keywordId: number;
+ page?: number;
+ size?: number;
+}
+
+export interface ResponseKeywordComment {
+ data: {
+ commentId: number;
+ comment: string;
+ createdAt: string;
+ }[];
+ hasNext: boolean;
+ nextPage: number;
+}
+
+export interface RequestHeadKeyword {
+ profileId: number;
+ oldHeadKeywordId: number;
+ keywordId: number;
+}
+
+export interface ResponseHeadKeyword {
+ profileId: number;
+ headKeyword: string;
+}
+
+export interface ResponseRandomKeywords {
+ requesterUuid: string;
+ keywords: {
+ keywordName: string;
+ count: number;
+ }[];
+}
+
+export interface RequestReportComment {
+ keywordCommentId: number;
+ reportType: 'HATE_SPEECH' | 'UNSUITABLE_KEYWORD' | 'OTHER';
+ reportEtc?: string;
+ content: string;
+}
diff --git a/src/utils/applicate.ts b/src/utils/applicate.ts
new file mode 100644
index 00000000..62b8ebcb
--- /dev/null
+++ b/src/utils/applicate.ts
@@ -0,0 +1,8 @@
+export const ApplicationStatus = {
+ MATCHED: 'MATCHED',
+ TEMP_SAVED: 'TEMP_SAVED',
+ MATCHING: 'MATCHING',
+ MATCH_FAILED: 'MATCH_FAILED',
+} as const;
+
+export type ApplicationStatusType = (typeof ApplicationStatus)[keyof typeof ApplicationStatus];
diff --git a/src/utils/auth.ts b/src/utils/auth.ts
new file mode 100644
index 00000000..c5d6fdd1
--- /dev/null
+++ b/src/utils/auth.ts
@@ -0,0 +1,20 @@
+// accessToken ๋ง๋ฃ ํ์ธ
+export function isTokenExpired(token?: string | null): boolean {
+ if (!token) return true;
+ try {
+ const payload = JSON.parse(atob(token.split('.')[1]));
+ if (!payload.exp) return true;
+ const now = Math.floor(Date.now() / 1000);
+ return payload.exp < now;
+ } catch {
+ return true;
+ }
+}
+
+// ๋ก๊ทธ์ธ์ํ ์ฒดํฌ
+export function isLoggedIn(userName: string | null | undefined): boolean {
+ if (!userName) return false;
+ const accessToken = typeof window !== 'undefined' ? localStorage.getItem('accessToken') : null;
+ if (!accessToken || isTokenExpired(accessToken)) return false;
+ return true;
+}
diff --git a/src/utils/cn.ts b/src/utils/cn.ts
new file mode 100644
index 00000000..2819a830
--- /dev/null
+++ b/src/utils/cn.ts
@@ -0,0 +1,6 @@
+import { clsx, type ClassValue } from 'clsx';
+import { twMerge } from 'tailwind-merge';
+
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs));
+}
diff --git a/src/utils/cookie.ts b/src/utils/cookie.ts
new file mode 100644
index 00000000..ccbafe4f
--- /dev/null
+++ b/src/utils/cookie.ts
@@ -0,0 +1,60 @@
+type CookieOptions = {
+ expires?: number;
+ path?: string;
+ domain?: string;
+ secure?: boolean;
+ sameSite?: 'strict' | 'lax' | 'none';
+};
+
+export function setCookie(name: string, value: string, options: CookieOptions = {}) {
+ if (typeof window === 'undefined') return;
+
+ const { expires = 365, path = '/', domain, secure = false, sameSite = 'lax' } = options;
+
+ let cookieString = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
+
+ if (expires) {
+ const date = new Date();
+ date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
+ cookieString += `; expires=${date.toUTCString()}`;
+ }
+
+ cookieString += `; path=${path}`;
+
+ if (domain) {
+ cookieString += `; domain=${domain}`;
+ }
+
+ if (secure) {
+ cookieString += '; secure';
+ }
+
+ cookieString += `; SameSite=${sameSite}`;
+
+ document.cookie = cookieString;
+}
+
+export function getCookie(name: string): string | null {
+ if (typeof window === 'undefined') return null;
+
+ const nameEQ = encodeURIComponent(name) + '=';
+ const ca = document.cookie.split(';');
+
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i];
+ while (c.charAt(0) === ' ') {
+ c = c.substring(1, c.length);
+ }
+ if (c.indexOf(nameEQ) === 0) {
+ return decodeURIComponent(c.substring(nameEQ.length, c.length));
+ }
+ }
+
+ return null;
+}
+
+export function removeCookie(name: string, path = '/') {
+ if (typeof window === 'undefined') return;
+
+ document.cookie = `${encodeURIComponent(name)}=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=${path};`;
+}
diff --git a/src/utils/position.ts b/src/utils/position.ts
new file mode 100644
index 00000000..e8e3e8b5
--- /dev/null
+++ b/src/utils/position.ts
@@ -0,0 +1,33 @@
+export const Position = {
+ ALL: 'ALL',
+ FRONTEND: 'FRONTEND',
+ BACKEND: 'BACKEND',
+ UI_UX: 'UI_UX',
+ AI: 'AI',
+ ANDROID: 'ANDROID',
+ IOS: 'IOS',
+ PLANNER: 'PLANNER',
+ MARKETER: 'MARKETER',
+ PM: 'PM',
+ CLOUD_INFRA: 'CLOUD_INFRA',
+ DEV_OPS: 'DEV_OPS',
+} as const;
+
+export type PositionType = (typeof Position)[keyof typeof Position];
+
+export const POSITION_VALUES = [
+ Position.ALL,
+ Position.FRONTEND,
+ Position.BACKEND,
+ Position.UI_UX,
+ Position.AI,
+ Position.ANDROID,
+ Position.IOS,
+ Position.PLANNER,
+ Position.MARKETER,
+ Position.PM,
+ Position.CLOUD_INFRA,
+ Position.DEV_OPS,
+] as const;
+
+export type PositionValueLiteral = (typeof POSITION_VALUES)[number];
diff --git a/src/utils/project.ts b/src/utils/project.ts
new file mode 100644
index 00000000..d460bbee
--- /dev/null
+++ b/src/utils/project.ts
@@ -0,0 +1,25 @@
+export const Period = {
+ WITHIN_1_MONTH: 'WITHIN_1_MONTH',
+ ONE_TO_THREE_MONTHS: 'ONE_TO_THREE_MONTHS',
+ THREE_TO_SIX_MONTHS: 'THREE_TO_SIX_MONTHS',
+ OVER_SIX_MONTHS: 'OVER_SIX_MONTHS',
+ FLEXIBLE: 'FLEXIBLE',
+} as const;
+
+export type PeriodType = (typeof Period)[keyof typeof Period];
+
+export const Status = {
+ OPEN: 'OPEN',
+ CONFIRMED: 'CONFIRMED',
+ CLOSED: 'CLOSED',
+} as const;
+
+export type StatusType = (typeof Status)[keyof typeof Status];
+
+export const ProgressWay = {
+ ONLINE: 'ONLINE',
+ OFFLINE: 'OFFLINE',
+ HYBRID: 'HYBRID',
+} as const;
+
+export type ProgressWayType = (typeof ProgressWay)[keyof typeof ProgressWay];
diff --git a/src/utils/project/formatDate.ts b/src/utils/project/formatDate.ts
new file mode 100644
index 00000000..260cf1ed
--- /dev/null
+++ b/src/utils/project/formatDate.ts
@@ -0,0 +1,53 @@
+export const parseDate = (dateString: string): Date | null => {
+ if (!dateString) return null;
+
+ const isoString = dateString.replace(/\./g, '-').replace(/ /g, 'T');
+
+ const date = new Date(isoString);
+
+ if (isNaN(date.getTime())) {
+ console.error('Failed to parse date:', dateString);
+ return null;
+ }
+
+ return date;
+};
+
+export const formatDate = (dateString: string) => {
+ const date = parseDate(dateString);
+ if (!date) return '';
+
+ const year = date.getFullYear();
+ const month = date.getMonth() + 1;
+ const day = date.getDate();
+
+ return `${year}๋
${month}์ ${day}์ผ`;
+};
+
+export const formatDateDot = (dateString: string): string => {
+ const date = parseDate(dateString);
+ if (!date) return '';
+
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, '0');
+ const day = String(date.getDate()).padStart(2, '0');
+
+ return `${year}.${month}.${day}`;
+};
+
+export const formatDday = (dday: number | null | undefined): string => {
+ if (dday == null) {
+ return '๋ง๊ฐ';
+ }
+
+ const numDday = Number(dday);
+
+ if (isNaN(numDday)) {
+ console.error('Invalid dday value:', dday);
+ return '๋ง๊ฐ';
+ }
+
+ if (numDday > 0) return `D-${numDday}`;
+ if (numDday === 0) return 'D-DAY';
+ return '๋ง๊ฐ';
+};
diff --git a/src/utils/project/formatPositions.ts b/src/utils/project/formatPositions.ts
new file mode 100644
index 00000000..c6787e1a
--- /dev/null
+++ b/src/utils/project/formatPositions.ts
@@ -0,0 +1,5 @@
+import { POSITION_KR } from '@/constants/Translate';
+
+export const formatPositions = (positions: Array<{ position: string; count: number }>) => {
+ return positions.map(({ position, count }) => `${POSITION_KR[position]} ${count}๋ช
`).join(', ');
+};
diff --git a/src/utils/project/getStatusColor.ts b/src/utils/project/getStatusColor.ts
new file mode 100644
index 00000000..d2e276d5
--- /dev/null
+++ b/src/utils/project/getStatusColor.ts
@@ -0,0 +1,12 @@
+export const getStatusColor = (status: string) => {
+ switch (status) {
+ case '๋งค์นญ์ค':
+ return 'bg-gray-200 text-gray-700';
+ case '๋งค์นญ์ฑ๊ณต':
+ return 'bg-blue-50 text-blue-200';
+ case '๋งค์นญ์คํจ':
+ return 'bg-red-10 text-red-100';
+ default:
+ return 'bg-gray-200 text-gray-700';
+ }
+};
diff --git a/src/utils/project/linkType.ts b/src/utils/project/linkType.ts
new file mode 100644
index 00000000..d45215d8
--- /dev/null
+++ b/src/utils/project/linkType.ts
@@ -0,0 +1,9 @@
+type LinkType = 'behance' | 'github' | 'notion' | 'linkedin' | 'other';
+
+export const getLinkType = (url: string): LinkType => {
+ if (url.includes('behance.net')) return 'behance';
+ if (url.includes('github.com')) return 'github';
+ if (url.includes('notion.so')) return 'notion';
+ if (url.includes('linkedin.com')) return 'linkedin';
+ return 'other';
+};
diff --git a/src/utils/project/mapStatustoEng.ts b/src/utils/project/mapStatustoEng.ts
new file mode 100644
index 00000000..08482f43
--- /dev/null
+++ b/src/utils/project/mapStatustoEng.ts
@@ -0,0 +1,12 @@
+const mapStatusToEng = (status: string) => {
+ switch (status) {
+ case '๋ชจ์ง์ค':
+ return 'OPEN';
+ case '๋ชจ์ง ๋ง๊ฐ':
+ return 'CLOSED';
+ default:
+ return status;
+ }
+};
+
+export default mapStatusToEng;
diff --git a/src/utils/project/positionLabel.ts b/src/utils/project/positionLabel.ts
new file mode 100644
index 00000000..d706b437
--- /dev/null
+++ b/src/utils/project/positionLabel.ts
@@ -0,0 +1,6 @@
+import { MY_TEAM_DROPDOWN } from '@/constants/Dropdown';
+
+export const getPositionLabel = (value: string) => {
+ const item = MY_TEAM_DROPDOWN.find((p) => p.value === value);
+ return item ? item.label : value;
+};
diff --git a/src/utils/project/validateUrl.ts b/src/utils/project/validateUrl.ts
new file mode 100644
index 00000000..41a821da
--- /dev/null
+++ b/src/utils/project/validateUrl.ts
@@ -0,0 +1,8 @@
+export const isValidUrl = (url: string): boolean => {
+ try {
+ const parsed = new URL(url);
+ return parsed.protocol === 'http:' || parsed.protocol === 'https:';
+ } catch {
+ return false;
+ }
+};
diff --git a/src/utils/workingTime.ts b/src/utils/workingTime.ts
new file mode 100644
index 00000000..85e072fd
--- /dev/null
+++ b/src/utils/workingTime.ts
@@ -0,0 +1,7 @@
+export const Time = {
+ MORNING: 'MORNING',
+ AFTERNOON: 'AFTERNOON',
+ EVENING: 'EVENING',
+} as const;
+
+export type TimeType = (typeof Time)[keyof typeof Time];
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 00000000..b575f7da
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,41 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": [
+ "./src/*"
+ ]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
+}