From 929c5c351707fb13fa7b9ec53ca8bff16cfe4ac1 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 00:11:30 +0900 Subject: [PATCH 01/28] =?UTF-8?q?=F0=9F=9A=9A=20rename:=20=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=EB=AA=85=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week5/mission1/.gitignore | 24 + sona/week5/mission1/README.md | 54 + sona/week5/mission1/eslint.config.js | 28 + sona/week5/mission1/index.html | 13 + sona/week5/mission1/package-lock.json | 3692 +++++++++++++++++ sona/week5/mission1/package.json | 36 + sona/week5/mission1/public/eye.svg | 3 + sona/week5/mission1/public/eye2.svg | 9 + sona/week5/mission1/public/eyeHalf.svg | 3 + sona/week5/mission1/public/eyeHalf2.svg | 9 + sona/week5/mission1/public/ggg-Photoroom.png | Bin 0 -> 64341 bytes sona/week5/mission1/public/profileImg.png | Bin 0 -> 1061 bytes sona/week5/mission1/src/App.css | 0 sona/week5/mission1/src/App.tsx | 16 + sona/week5/mission1/src/apis/auth.ts | 35 + sona/week5/mission1/src/apis/axios.ts | 118 + sona/week5/mission1/src/components/Header.tsx | 31 + .../mission1/src/components/InputField.tsx | 33 + sona/week5/mission1/src/constants/key.ts | 4 + .../mission1/src/context/AuthContext.tsx | 88 + sona/week5/mission1/src/hooks/useForm.tsx | 50 + .../mission1/src/hooks/useLocalStorage.ts | 28 + sona/week5/mission1/src/index.css | 10 + .../mission1/src/layout/HomePage.tsx | 0 .../mission1/src/layout/ProtectedLayout.tsx | 14 + sona/week5/mission1/src/main.tsx | 10 + .../src/pages/GoogleLoginRedirectPage.tsx | 26 + sona/week5/mission1/src/pages/Login.tsx | 97 + sona/week5/mission1/src/pages/MyPage.tsx | 44 + sona/week5/mission1/src/pages/Navbar.tsx | 19 + .../week5/mission1/src/pages/NotFoundPage.tsx | 3 + sona/week5/mission1/src/pages/SignUp.tsx | 121 + sona/week5/mission1/src/routes.tsx | 39 + sona/week5/mission1/src/types/auth.ts | 44 + sona/week5/mission1/src/types/common.ts | 6 + sona/week5/mission1/src/utils/validate.ts | 34 + sona/week5/mission1/src/vite-env.d.ts | 9 + sona/week5/mission1/tsconfig.app.json | 26 + sona/week5/mission1/tsconfig.json | 7 + sona/week5/mission1/tsconfig.node.json | 24 + sona/week5/mission1/vite.config.ts | 8 + sona/week7/mission1/index.html | 2 +- sona/week7/mission1/package-lock.json | 71 + sona/week7/mission1/package.json | 3 + sona/week7/mission1/public/detailHart.svg | 10 + sona/week7/mission1/public/hambugi.svg | 3 + sona/week7/mission1/public/hart.svg | 3 + sona/week7/mission1/public/search.svg | 3 + sona/week7/mission1/src/App.tsx | 20 +- sona/week7/mission1/src/apis/comment.ts | 14 + sona/week7/mission1/src/apis/lp.ts | 13 + sona/week7/mission1/src/components/Header.tsx | 4 +- sona/week7/mission1/src/constants/key.ts | 7 + sona/week7/mission1/src/enums/common.ts | 4 + .../mission1/src/hooks/useGetCommentList.tsx | 15 + sona/week7/mission1/src/hooks/useGetLpList.ts | 30 + .../mission1/src/hooks/useGetProfile.tsx | 16 + .../src/hooks/usegetInfiniteCommentList.ts | 22 + .../src/hooks/usegetInfiniteLpList.ts | 21 + sona/week7/mission1/src/index.css | 9 + sona/week7/mission1/src/layout/HomeLayout.tsx | 13 + .../mission1/src/layout/ProtectedLayout.tsx | 9 +- sona/week7/mission1/src/main.tsx | 14 +- sona/week7/mission1/src/pages/CardDetail.tsx | 68 + sona/week7/mission1/src/pages/Comment.tsx | 92 + sona/week7/mission1/src/pages/CommentItem.tsx | 28 + .../mission1/src/pages/CommentSkeleton.tsx | 14 + sona/week7/mission1/src/pages/Home.tsx | 72 + sona/week7/mission1/src/pages/Login.tsx | 106 +- sona/week7/mission1/src/pages/LpCard.tsx | 40 + .../mission1/src/pages/LpCardSkeleton.tsx | 19 + sona/week7/mission1/src/pages/Navbar.tsx | 93 +- sona/week7/mission1/src/pages/SignUp.tsx | 117 +- sona/week7/mission1/src/routes.tsx | 23 +- sona/week7/mission1/src/types/comment.ts | 23 + sona/week7/mission1/src/types/common.ts | 24 + sona/week7/mission1/src/types/lp.ts | 27 + .../week7/mission1/src/utils/dateCalculate.ts | 21 + 78 files changed, 5750 insertions(+), 138 deletions(-) create mode 100644 sona/week5/mission1/.gitignore create mode 100644 sona/week5/mission1/README.md create mode 100644 sona/week5/mission1/eslint.config.js create mode 100644 sona/week5/mission1/index.html create mode 100644 sona/week5/mission1/package-lock.json create mode 100644 sona/week5/mission1/package.json create mode 100644 sona/week5/mission1/public/eye.svg create mode 100644 sona/week5/mission1/public/eye2.svg create mode 100644 sona/week5/mission1/public/eyeHalf.svg create mode 100644 sona/week5/mission1/public/eyeHalf2.svg create mode 100644 sona/week5/mission1/public/ggg-Photoroom.png create mode 100644 sona/week5/mission1/public/profileImg.png create mode 100644 sona/week5/mission1/src/App.css create mode 100644 sona/week5/mission1/src/App.tsx create mode 100644 sona/week5/mission1/src/apis/auth.ts create mode 100644 sona/week5/mission1/src/apis/axios.ts create mode 100644 sona/week5/mission1/src/components/Header.tsx create mode 100644 sona/week5/mission1/src/components/InputField.tsx create mode 100644 sona/week5/mission1/src/constants/key.ts create mode 100644 sona/week5/mission1/src/context/AuthContext.tsx create mode 100644 sona/week5/mission1/src/hooks/useForm.tsx create mode 100644 sona/week5/mission1/src/hooks/useLocalStorage.ts create mode 100644 sona/week5/mission1/src/index.css rename sona/{week7 => week5}/mission1/src/layout/HomePage.tsx (100%) create mode 100644 sona/week5/mission1/src/layout/ProtectedLayout.tsx create mode 100644 sona/week5/mission1/src/main.tsx create mode 100644 sona/week5/mission1/src/pages/GoogleLoginRedirectPage.tsx create mode 100644 sona/week5/mission1/src/pages/Login.tsx create mode 100644 sona/week5/mission1/src/pages/MyPage.tsx create mode 100644 sona/week5/mission1/src/pages/Navbar.tsx create mode 100644 sona/week5/mission1/src/pages/NotFoundPage.tsx create mode 100644 sona/week5/mission1/src/pages/SignUp.tsx create mode 100644 sona/week5/mission1/src/routes.tsx create mode 100644 sona/week5/mission1/src/types/auth.ts create mode 100644 sona/week5/mission1/src/types/common.ts create mode 100644 sona/week5/mission1/src/utils/validate.ts create mode 100644 sona/week5/mission1/src/vite-env.d.ts create mode 100644 sona/week5/mission1/tsconfig.app.json create mode 100644 sona/week5/mission1/tsconfig.json create mode 100644 sona/week5/mission1/tsconfig.node.json create mode 100644 sona/week5/mission1/vite.config.ts create mode 100644 sona/week7/mission1/public/detailHart.svg create mode 100644 sona/week7/mission1/public/hambugi.svg create mode 100644 sona/week7/mission1/public/hart.svg create mode 100644 sona/week7/mission1/public/search.svg create mode 100644 sona/week7/mission1/src/apis/comment.ts create mode 100644 sona/week7/mission1/src/apis/lp.ts create mode 100644 sona/week7/mission1/src/enums/common.ts create mode 100644 sona/week7/mission1/src/hooks/useGetCommentList.tsx create mode 100644 sona/week7/mission1/src/hooks/useGetLpList.ts create mode 100644 sona/week7/mission1/src/hooks/useGetProfile.tsx create mode 100644 sona/week7/mission1/src/hooks/usegetInfiniteCommentList.ts create mode 100644 sona/week7/mission1/src/hooks/usegetInfiniteLpList.ts create mode 100644 sona/week7/mission1/src/layout/HomeLayout.tsx create mode 100644 sona/week7/mission1/src/pages/CardDetail.tsx create mode 100644 sona/week7/mission1/src/pages/Comment.tsx create mode 100644 sona/week7/mission1/src/pages/CommentItem.tsx create mode 100644 sona/week7/mission1/src/pages/CommentSkeleton.tsx create mode 100644 sona/week7/mission1/src/pages/Home.tsx create mode 100644 sona/week7/mission1/src/pages/LpCard.tsx create mode 100644 sona/week7/mission1/src/pages/LpCardSkeleton.tsx create mode 100644 sona/week7/mission1/src/types/comment.ts create mode 100644 sona/week7/mission1/src/types/lp.ts create mode 100644 sona/week7/mission1/src/utils/dateCalculate.ts diff --git a/sona/week5/mission1/.gitignore b/sona/week5/mission1/.gitignore new file mode 100644 index 00000000..83e1c004 --- /dev/null +++ b/sona/week5/mission1/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* +.env +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/sona/week5/mission1/README.md b/sona/week5/mission1/README.md new file mode 100644 index 00000000..40ede56e --- /dev/null +++ b/sona/week5/mission1/README.md @@ -0,0 +1,54 @@ +# React + TypeScript + Vite + +This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules. + +Currently, two official plugins are available: + +- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh +- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh + +## Expanding the ESLint configuration + +If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules: + +```js +export default tseslint.config({ + extends: [ + // Remove ...tseslint.configs.recommended and replace with this + ...tseslint.configs.recommendedTypeChecked, + // Alternatively, use this for stricter rules + ...tseslint.configs.strictTypeChecked, + // Optionally, add this for stylistic rules + ...tseslint.configs.stylisticTypeChecked, + ], + languageOptions: { + // other options... + parserOptions: { + project: ['./tsconfig.node.json', './tsconfig.app.json'], + tsconfigRootDir: import.meta.dirname, + }, + }, +}) +``` + +You can also install [eslint-plugin-react-x](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-x) and [eslint-plugin-react-dom](https://github.com/Rel1cx/eslint-react/tree/main/packages/plugins/eslint-plugin-react-dom) for React-specific lint rules: + +```js +// eslint.config.js +import reactX from 'eslint-plugin-react-x' +import reactDom from 'eslint-plugin-react-dom' + +export default tseslint.config({ + plugins: { + // Add the react-x and react-dom plugins + 'react-x': reactX, + 'react-dom': reactDom, + }, + rules: { + // other rules... + // Enable its recommended typescript rules + ...reactX.configs['recommended-typescript'].rules, + ...reactDom.configs.recommended.rules, + }, +}) +``` diff --git a/sona/week5/mission1/eslint.config.js b/sona/week5/mission1/eslint.config.js new file mode 100644 index 00000000..092408a9 --- /dev/null +++ b/sona/week5/mission1/eslint.config.js @@ -0,0 +1,28 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' + +export default tseslint.config( + { ignores: ['dist'] }, + { + extends: [js.configs.recommended, ...tseslint.configs.recommended], + files: ['**/*.{ts,tsx}'], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + plugins: { + 'react-hooks': reactHooks, + 'react-refresh': reactRefresh, + }, + rules: { + ...reactHooks.configs.recommended.rules, + 'react-refresh/only-export-components': [ + 'warn', + { allowConstantExport: true }, + ], + }, + }, +) diff --git a/sona/week5/mission1/index.html b/sona/week5/mission1/index.html new file mode 100644 index 00000000..420ff807 --- /dev/null +++ b/sona/week5/mission1/index.html @@ -0,0 +1,13 @@ + + + + + + + mission1 + + +
+ + + diff --git a/sona/week5/mission1/package-lock.json b/sona/week5/mission1/package-lock.json new file mode 100644 index 00000000..f7fe679d --- /dev/null +++ b/sona/week5/mission1/package-lock.json @@ -0,0 +1,3692 @@ +{ + "name": "ka", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "ka", + "version": "0.0.0", + "dependencies": { + "@hookform/resolvers": "^5.0.1", + "@tailwindcss/vite": "^4.1.3", + "axios": "^1.8.4", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-hook-form": "^7.55.0", + "react-router-dom": "^6.30.0", + "tailwindcss": "^4.1.3", + "zod": "^3.24.3" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react-swc": "^3.8.0", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.2.tgz", + "integrity": "sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.2.tgz", + "integrity": "sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.2.tgz", + "integrity": "sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.2.tgz", + "integrity": "sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.2.tgz", + "integrity": "sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.2.tgz", + "integrity": "sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.2.tgz", + "integrity": "sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.2.tgz", + "integrity": "sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.2.tgz", + "integrity": "sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.2.tgz", + "integrity": "sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.2.tgz", + "integrity": "sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.2.tgz", + "integrity": "sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.2.tgz", + "integrity": "sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.2.tgz", + "integrity": "sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.2.tgz", + "integrity": "sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.2.tgz", + "integrity": "sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.2.tgz", + "integrity": "sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.2.tgz", + "integrity": "sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.2.tgz", + "integrity": "sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.2.tgz", + "integrity": "sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.2.tgz", + "integrity": "sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.2.tgz", + "integrity": "sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.2.tgz", + "integrity": "sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.2.tgz", + "integrity": "sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.2.tgz", + "integrity": "sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.5.1.tgz", + "integrity": "sha512-soEIOALTfTK6EjmKMMoLugwaP0rzkad90iIWd1hMO9ARkSAyjfMfkRRhLvD5qH7vvM0Cg72pieUfR6yh6XxC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.1.tgz", + "integrity": "sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/js": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.24.0.tgz", + "integrity": "sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz", + "integrity": "sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.13.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.13.0.tgz", + "integrity": "sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@hookform/resolvers": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-5.0.1.tgz", + "integrity": "sha512-u/+Jp83luQNx9AdyW2fIPGY6Y7NG68eN2ZW8FOJYL+M0i4s49+refdJdOp/A9n9HFQtQs3HIDHQvX3ZET2o7YA==", + "license": "MIT", + "dependencies": { + "@standard-schema/utils": "^0.3.0" + }, + "peerDependencies": { + "react-hook-form": "^7.55.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@remix-run/router": { + "version": "1.23.0", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.0.tgz", + "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.39.0.tgz", + "integrity": "sha512-lGVys55Qb00Wvh8DMAocp5kIcaNzEFTmGhfFd88LfaogYTRKrdxgtlO5H6S49v2Nd8R2C6wLOal0qv6/kCkOwA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.39.0.tgz", + "integrity": "sha512-It9+M1zE31KWfqh/0cJLrrsCPiF72PoJjIChLX+rEcujVRCb4NLQ5QzFkzIZW8Kn8FTbvGQBY5TkKBau3S8cCQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.39.0.tgz", + "integrity": "sha512-lXQnhpFDOKDXiGxsU9/l8UEGGM65comrQuZ+lDcGUx+9YQ9dKpF3rSEGepyeR5AHZ0b5RgiligsBhWZfSSQh8Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.39.0.tgz", + "integrity": "sha512-mKXpNZLvtEbgu6WCkNij7CGycdw9cJi2k9v0noMb++Vab12GZjFgUXD69ilAbBh034Zwn95c2PNSz9xM7KYEAQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.39.0.tgz", + "integrity": "sha512-jivRRlh2Lod/KvDZx2zUR+I4iBfHcu2V/BA2vasUtdtTN2Uk3jfcZczLa81ESHZHPHy4ih3T/W5rPFZ/hX7RtQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.39.0.tgz", + "integrity": "sha512-8RXIWvYIRK9nO+bhVz8DwLBepcptw633gv/QT4015CpJ0Ht8punmoHU/DuEd3iw9Hr8UwUV+t+VNNuZIWYeY7Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.39.0.tgz", + "integrity": "sha512-mz5POx5Zu58f2xAG5RaRRhp3IZDK7zXGk5sdEDj4o96HeaXhlUwmLFzNlc4hCQi5sGdR12VDgEUqVSHer0lI9g==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.39.0.tgz", + "integrity": "sha512-+YDwhM6gUAyakl0CD+bMFpdmwIoRDzZYaTWV3SDRBGkMU/VpIBYXXEvkEcTagw/7VVkL2vA29zU4UVy1mP0/Yw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.39.0.tgz", + "integrity": "sha512-EKf7iF7aK36eEChvlgxGnk7pdJfzfQbNvGV/+l98iiMwU23MwvmV0Ty3pJ0p5WQfm3JRHOytSIqD9LB7Bq7xdQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.39.0.tgz", + "integrity": "sha512-vYanR6MtqC7Z2SNr8gzVnzUul09Wi1kZqJaek3KcIlI/wq5Xtq4ZPIZ0Mr/st/sv/NnaPwy/D4yXg5x0B3aUUA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loongarch64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.39.0.tgz", + "integrity": "sha512-NMRUT40+h0FBa5fb+cpxtZoGAggRem16ocVKIv5gDB5uLDgBIwrIsXlGqYbLwW8YyO3WVTk1FkFDjMETYlDqiw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.39.0.tgz", + "integrity": "sha512-0pCNnmxgduJ3YRt+D+kJ6Ai/r+TaePu9ZLENl+ZDV/CdVczXl95CbIiwwswu4L+K7uOIGf6tMo2vm8uadRaICQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.39.0.tgz", + "integrity": "sha512-t7j5Zhr7S4bBtksT73bO6c3Qa2AV/HqiGlj9+KB3gNF5upcVkx+HLgxTm8DK4OkzsOYqbdqbLKwvGMhylJCPhQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.39.0.tgz", + "integrity": "sha512-m6cwI86IvQ7M93MQ2RF5SP8tUjD39Y7rjb1qjHgYh28uAPVU8+k/xYWvxRO3/tBN2pZkSMa5RjnPuUIbrwVxeA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.39.0.tgz", + "integrity": "sha512-iRDJd2ebMunnk2rsSBYlsptCyuINvxUfGwOUldjv5M4tpa93K8tFMeYGpNk2+Nxl+OBJnBzy2/JCscGeO507kA==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.39.0.tgz", + "integrity": "sha512-t9jqYw27R6Lx0XKfEFe5vUeEJ5pF3SGIM6gTfONSMb7DuG6z6wfj2yjcoZxHg129veTqU7+wOhY6GX8wmf90dA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.39.0.tgz", + "integrity": "sha512-ThFdkrFDP55AIsIZDKSBWEt/JcWlCzydbZHinZ0F/r1h83qbGeenCt/G/wG2O0reuENDD2tawfAj2s8VK7Bugg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.39.0.tgz", + "integrity": "sha512-jDrLm6yUtbOg2TYB3sBF3acUnAwsIksEYjLeHL+TJv9jg+TmTwdyjnDex27jqEMakNKf3RwwPahDIt7QXCSqRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.39.0.tgz", + "integrity": "sha512-6w9uMuza+LbLCVoNKL5FSLE7yvYkq9laSd09bwS0tMjkwXrmib/4KmoJcrKhLWHvw19mwU+33ndC69T7weNNjQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.39.0.tgz", + "integrity": "sha512-yAkUOkIKZlK5dl7u6dg897doBgLXmUHhIINM2c+sND3DZwnrdQkkSiDh7N75Ll4mM4dxSkYfXqU9fW3lLkMFug==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@standard-schema/utils": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz", + "integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==", + "license": "MIT" + }, + "node_modules/@swc/core": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.11.18.tgz", + "integrity": "sha512-ORZxyCKKiqYt2iHdh1C7pfVR1GBjkuFOdwqZggQzaq0vt22DpGca+2JsUtkUoWQmWcct04v5+ScwgvsHuMObxA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.21" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.11.18", + "@swc/core-darwin-x64": "1.11.18", + "@swc/core-linux-arm-gnueabihf": "1.11.18", + "@swc/core-linux-arm64-gnu": "1.11.18", + "@swc/core-linux-arm64-musl": "1.11.18", + "@swc/core-linux-x64-gnu": "1.11.18", + "@swc/core-linux-x64-musl": "1.11.18", + "@swc/core-win32-arm64-msvc": "1.11.18", + "@swc/core-win32-ia32-msvc": "1.11.18", + "@swc/core-win32-x64-msvc": "1.11.18" + }, + "peerDependencies": { + "@swc/helpers": "*" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.11.18.tgz", + "integrity": "sha512-K6AntdUlNMQg8aChqjeXwnVhK6d4WRZ9TgtLSTmdU0Ugll4an7QK49s9NrT7XQU91cEsVvzdr++p1bNImx0hJg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.11.18.tgz", + "integrity": "sha512-RCRvC6Q9M5BArTvj/IzUAAYGrgxYFbTTnAtf6UX7JFq2DAn+hEwYUjmC1m0gFso9HqFU0m5QZUGfZvVmACGWUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.11.18.tgz", + "integrity": "sha512-wteAKf8YKb3jOnZFm3EzuIMzzCVXMuQOLHsz1IgEOc44/gdgNXKxaYTWAowZuej7t68tf/w0cRNMc7Le414v/g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.11.18.tgz", + "integrity": "sha512-hY6jJYZ6PKHSBo5OATswfyKsUgsWu9+4nDcN8liYIRRgz3E0G9wk0VUTP4cFPivBFeHWTTAGz687/Nf2aQEIpw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.11.18.tgz", + "integrity": "sha512-slu0mlP2nucvQalttnapfpqpD/LlM9NHx9g3ofgsLzjObyMEBiX4ZysQ3y65U8Mjw71RNqtLd/ZmvxI6OmLdiQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.11.18.tgz", + "integrity": "sha512-h9a/8PA25arMCQ9t8CE8rA1s0c77z4kCZZ7dUuUkD88yEXIrARMca1IKR7of+S3slfQrf1Zlq3Ac1Fb1HVJziQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.11.18.tgz", + "integrity": "sha512-0sMDJj5qUGK9QEw4lrxLxkTP/4AoKciqNzXvqbk+J9XuXN2aIv4BsR1Y7z3GwAeMFGsba2lbHLOtJlDsaqIsiA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.11.18.tgz", + "integrity": "sha512-zGv9HnfgBcKyt54MJRWdwRNu9BuYkAFM7bx+tWtKhd37Ef7ZX20QLs9xXl5wWDXCbsOdRxXIZgXs6PEL+Pzmrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.11.18.tgz", + "integrity": "sha512-uBKj0S1lYv/E2ZhxHZOxSiQwoegYmzbPRpjq6eHBZDv97mu7W3K27/lsnPbvAfQ6b6rnv8BI+EsmJ7VLQBAHBQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.11.18", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.11.18.tgz", + "integrity": "sha512-8USTRcdgeFMNBgvVXl8tz6n4+9s9m+zHsfDeBT4jPgwnq2bnLBlTUlwnPwzDxfg9nUJr6RFD4xeKfWyZZRosZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@swc/types": { + "version": "0.1.21", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.21.tgz", + "integrity": "sha512-2YEtj5HJVbKivud9N4bpPBAyZhj4S2Ipe5LkUG94alTpr7in/GU/EARgPAd3BwU+YOmFVJC2+kjqhGRi3r0ZpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, + "node_modules/@tailwindcss/node": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.3.tgz", + "integrity": "sha512-H/6r6IPFJkCfBJZ2dKZiPJ7Ueb2wbL592+9bQEl2r73qbX6yGnmQVIfiUvDRB2YI0a3PWDrzUwkvQx1XW1bNkA==", + "license": "MIT", + "dependencies": { + "enhanced-resolve": "^5.18.1", + "jiti": "^2.4.2", + "lightningcss": "1.29.2", + "tailwindcss": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.3.tgz", + "integrity": "sha512-t16lpHCU7LBxDe/8dCj9ntyNpXaSTAgxWm1u2XQP5NiIu4KGSyrDJJRlK9hJ4U9yJxx0UKCVI67MJWFNll5mOQ==", + "license": "MIT", + "engines": { + "node": ">= 10" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-arm64": "4.1.3", + "@tailwindcss/oxide-darwin-x64": "4.1.3", + "@tailwindcss/oxide-freebsd-x64": "4.1.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.1.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.1.3", + "@tailwindcss/oxide-linux-x64-musl": "4.1.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.1.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.1.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.3.tgz", + "integrity": "sha512-cxklKjtNLwFl3mDYw4XpEfBY+G8ssSg9ADL4Wm6//5woi3XGqlxFsnV5Zb6v07dxw1NvEX2uoqsxO/zWQsgR+g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.3.tgz", + "integrity": "sha512-mqkf2tLR5VCrjBvuRDwzKNShRu99gCAVMkVsaEOFvv6cCjlEKXRecPu9DEnxp6STk5z+Vlbh1M5zY3nQCXMXhw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.3.tgz", + "integrity": "sha512-7sGraGaWzXvCLyxrc7d+CCpUN3fYnkkcso3rCzwUmo/LteAl2ZGCDlGvDD8Y/1D3ngxT8KgDj1DSwOnNewKhmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.3.tgz", + "integrity": "sha512-E2+PbcbzIReaAYZe997wb9rId246yDkCwAakllAWSGqe6VTg9hHle67hfH6ExjpV2LSK/siRzBUs5wVff3RW9w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.3.tgz", + "integrity": "sha512-GvfbJ8wjSSjbLFFE3UYz4Eh8i4L6GiEYqCtA8j2Zd2oXriPuom/Ah/64pg/szWycQpzRnbDiJozoxFU2oJZyfg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.3.tgz", + "integrity": "sha512-35UkuCWQTeG9BHcBQXndDOrpsnt3Pj9NVIB4CgNiKmpG8GnCNXeMczkUpOoqcOhO6Cc/mM2W7kaQ/MTEENDDXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.3.tgz", + "integrity": "sha512-dm18aQiML5QCj9DQo7wMbt1Z2tl3Giht54uVR87a84X8qRtuXxUqnKQkRDK5B4bCOmcZ580lF9YcoMkbDYTXHQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.3.tgz", + "integrity": "sha512-LMdTmGe/NPtGOaOfV2HuO7w07jI3cflPrVq5CXl+2O93DCewADK0uW1ORNAcfu2YxDUS035eY2W38TxrsqngxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.3.tgz", + "integrity": "sha512-aalNWwIi54bbFEizwl1/XpmdDrOaCjRFQRgtbv9slWjmNPuJJTIKPHf5/XXDARc9CneW9FkSTqTbyvNecYAEGw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.3.tgz", + "integrity": "sha512-PEj7XR4OGTGoboTIAdXicKuWl4EQIjKHKuR+bFy9oYN7CFZo0eu74+70O4XuERX4yjqVZGAkCdglBODlgqcCXg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.3.tgz", + "integrity": "sha512-T8gfxECWDBENotpw3HR9SmNiHC9AOJdxs+woasRZ8Q/J4VHN0OMs7F+4yVNZ9EVN26Wv6mZbK0jv7eHYuLJLwA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.1.3.tgz", + "integrity": "sha512-lUI/QaDxLtlV52Lho6pu07CG9pSnRYLOPmKGIQjyHdTBagemc6HmgZxyjGAQ/5HMPrNeWBfTVIpQl0/jLXvWHQ==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.1.3", + "@tailwindcss/oxide": "4.1.3", + "tailwindcss": "4.1.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6" + } + }, + "node_modules/@types/estree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", + "integrity": "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==", + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.0.tgz", + "integrity": "sha512-UaicktuQI+9UKyA4njtDOGBD/67t8YEBt2xdfqu8+gP9hqPUPsiXlNPcpS2gVdjmis5GKPG3fCxbQLVgxsQZ8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.1", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.1.tgz", + "integrity": "sha512-jFf/woGTVTjUJsl2O7hcopJ1r0upqoq/vIOoCj0yLh3RIXxWcljlpuZ+vEBRXsymD1jhfeJrlyTy/S1UW+4y1w==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.29.1.tgz", + "integrity": "sha512-ba0rr4Wfvg23vERs3eB+P3lfj2E+2g3lhWcCVukUuhtcdUx5lSIFZlGFEBHKr+3zizDa/TvZTptdNHVZWAkSBg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/type-utils": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "graphemer": "^1.4.0", + "ignore": "^5.3.1", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.29.1.tgz", + "integrity": "sha512-zczrHVEqEaTwh12gWBIJWj8nx+ayDcCJs06yoNMY0kwjMWDM6+kppljY+BxWI06d2Ja+h4+WdufDcwMnnMEWmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.29.1.tgz", + "integrity": "sha512-2nggXGX5F3YrsGN08pw4XpMLO1Rgtnn4AzTegC2MDesv6q3QaTU5yU7IbS1tf1IwCR0Hv/1EFygLn9ms6LIpDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.29.1.tgz", + "integrity": "sha512-DkDUSDwZVCYN71xA4wzySqqcZsHKic53A4BLqmrWFFpOpNSoxX233lwGu/2135ymTCR04PoKiEEEvN1gFYg4Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.29.1", + "@typescript-eslint/utils": "8.29.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.29.1.tgz", + "integrity": "sha512-VT7T1PuJF1hpYC3AGm2rCgJBjHL3nc+A/bhOp9sGMKfi5v0WufsX/sHCFBfNTx2F+zA6qBc/PD0/kLRLjdt8mQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.29.1.tgz", + "integrity": "sha512-l1enRoSaUkQxOQnbi0KPUtqeZkSiFlqrx9/3ns2rEDhGKfTa+88RmXqedC1zmVTOWrLc2e6DEJrTA51C9iLH5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/visitor-keys": "8.29.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.0.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.29.1.tgz", + "integrity": "sha512-QAkFEbytSaB8wnmB+DflhUPz6CLbFWE2SnSCrRMEa+KnXIzDYbpsn++1HGvnfAsUY44doDXmvRkO5shlM/3UfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.29.1", + "@typescript-eslint/types": "8.29.1", + "@typescript-eslint/typescript-estree": "8.29.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.29.1.tgz", + "integrity": "sha512-RGLh5CRaUEf02viP5c1Vh1cMGffQscyHe7HPAzGpfmfflFg1wUz2rYxd+OZqwpeypYvZ8UxSxuIpF++fmOzEcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.29.1", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react-swc": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react-swc/-/plugin-react-swc-3.8.1.tgz", + "integrity": "sha512-aEUPCckHDcFyxpwFm0AIkbtv6PpUp3xTb9wYGFjtABynXjCYKkWoxX0AOK9NT9XCrdk6mBBUOeHQS+RKdcNO1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@swc/core": "^1.11.11" + }, + "peerDependencies": { + "vite": "^4 || ^5 || ^6" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/axios": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", + "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", + "integrity": "sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.18.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz", + "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.2.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.2.tgz", + "integrity": "sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.2", + "@esbuild/android-arm": "0.25.2", + "@esbuild/android-arm64": "0.25.2", + "@esbuild/android-x64": "0.25.2", + "@esbuild/darwin-arm64": "0.25.2", + "@esbuild/darwin-x64": "0.25.2", + "@esbuild/freebsd-arm64": "0.25.2", + "@esbuild/freebsd-x64": "0.25.2", + "@esbuild/linux-arm": "0.25.2", + "@esbuild/linux-arm64": "0.25.2", + "@esbuild/linux-ia32": "0.25.2", + "@esbuild/linux-loong64": "0.25.2", + "@esbuild/linux-mips64el": "0.25.2", + "@esbuild/linux-ppc64": "0.25.2", + "@esbuild/linux-riscv64": "0.25.2", + "@esbuild/linux-s390x": "0.25.2", + "@esbuild/linux-x64": "0.25.2", + "@esbuild/netbsd-arm64": "0.25.2", + "@esbuild/netbsd-x64": "0.25.2", + "@esbuild/openbsd-arm64": "0.25.2", + "@esbuild/openbsd-x64": "0.25.2", + "@esbuild/sunos-x64": "0.25.2", + "@esbuild/win32-arm64": "0.25.2", + "@esbuild/win32-ia32": "0.25.2", + "@esbuild/win32-x64": "0.25.2" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.24.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.24.0.tgz", + "integrity": "sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.24.0", + "@eslint/plugin-kit": "^0.2.7", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "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.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "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" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.19.tgz", + "integrity": "sha512-eyy8pcr/YxSYjBoqIFSrlbn9i/xvxUFa8CjzAYo9cFjgGXqq1hyjihcpZvxRLalpaWmueWR81xn7vuKmAFijDQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": ">=8.40" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", + "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.29.2.tgz", + "integrity": "sha512-6b6gd/RUXKaw5keVdSEtqFVdzWnU5jMxTUjA2bVcMNPLwSQ08Sv/UodBVtETLCn7k4S1Ibxwh7k68IwLZPgKaA==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-darwin-arm64": "1.29.2", + "lightningcss-darwin-x64": "1.29.2", + "lightningcss-freebsd-x64": "1.29.2", + "lightningcss-linux-arm-gnueabihf": "1.29.2", + "lightningcss-linux-arm64-gnu": "1.29.2", + "lightningcss-linux-arm64-musl": "1.29.2", + "lightningcss-linux-x64-gnu": "1.29.2", + "lightningcss-linux-x64-musl": "1.29.2", + "lightningcss-win32-arm64-msvc": "1.29.2", + "lightningcss-win32-x64-msvc": "1.29.2" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.29.2.tgz", + "integrity": "sha512-cK/eMabSViKn/PG8U/a7aCorpeKLMlK0bQeNHmdb7qUnBkNPnL+oV5DjJUo0kqWsJUapZsM4jCfYItbqBDvlcA==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.29.2.tgz", + "integrity": "sha512-j5qYxamyQw4kDXX5hnnCKMf3mLlHvG44f24Qyi2965/Ycz829MYqjrVg2H8BidybHBp9kom4D7DR5VqCKDXS0w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.29.2.tgz", + "integrity": "sha512-wDk7M2tM78Ii8ek9YjnY8MjV5f5JN2qNVO+/0BAGZRvXKtQrBC4/cn4ssQIpKIPP44YXw6gFdpUF+Ps+RGsCwg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.29.2.tgz", + "integrity": "sha512-IRUrOrAF2Z+KExdExe3Rz7NSTuuJ2HvCGlMKoquK5pjvo2JY4Rybr+NrKnq0U0hZnx5AnGsuFHjGnNT14w26sg==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.29.2.tgz", + "integrity": "sha512-KKCpOlmhdjvUTX/mBuaKemp0oeDIBBLFiU5Fnqxh1/DZ4JPZi4evEH7TKoSBFOSOV3J7iEmmBaw/8dpiUvRKlQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.29.2.tgz", + "integrity": "sha512-Q64eM1bPlOOUgxFmoPUefqzY1yV3ctFPE6d/Vt7WzLW4rKTv7MyYNky+FWxRpLkNASTnKQUaiMJ87zNODIrrKQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.29.2.tgz", + "integrity": "sha512-0v6idDCPG6epLXtBH/RPkHvYx74CVziHo6TMYga8O2EiQApnUPZsbR9nFNrg2cgBzk1AYqEd95TlrsL7nYABQg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.29.2.tgz", + "integrity": "sha512-rMpz2yawkgGT8RULc5S4WiZopVMOFWjiItBT7aSfDX4NQav6M44rhn5hjtkKzB+wMTRlLLqxkeYEtQ3dd9696w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.29.2.tgz", + "integrity": "sha512-nL7zRW6evGQqYVu/bKGK+zShyz8OVzsCotFgc7judbt6wnB2KbiKKJwBE4SGoDBQ1O94RjW4asrCjQL4i8Fhbw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.29.2", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.29.2.tgz", + "integrity": "sha512-EdIUW3B2vLuHmv7urfzMI/h2fmlnOQBk1xlsDxkN1tCWKjNFjfLhGxYk8C8mzpSfr+A6jFFIi8fU6LbQGsRWjA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", + "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz", + "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.26.0" + }, + "peerDependencies": { + "react": "^19.1.0" + } + }, + "node_modules/react-hook-form": { + "version": "7.55.0", + "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.55.0.tgz", + "integrity": "sha512-XRnjsH3GVMQz1moZTW53MxfoWN7aDpUg/GpVNc4A3eXRVNdGXfbzJ4vM4aLQ8g6XCUh1nIbx70aaNCl7kxnjog==", + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/react-hook-form" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17 || ^18 || ^19" + } + }, + "node_modules/react-router": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", + "integrity": "sha512-D3X8FyH9nBcTSHGdEKurK7r8OYE1kKFn3d/CF+CoxbSHkxU7o37+Uh7eAHRXr6k2tSExXYO++07PeXJtA/dEhQ==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.30.0", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.30.0.tgz", + "integrity": "sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==", + "license": "MIT", + "dependencies": { + "@remix-run/router": "1.23.0", + "react-router": "6.30.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.39.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.39.0.tgz", + "integrity": "sha512-thI8kNc02yNvnmJp8dr3fNWJ9tCONDhp6TV35X6HkKGGs9E6q7YWCHbe5vKiTa7TAiNcFEmXKj3X/pG2b3ci0g==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.7" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.39.0", + "@rollup/rollup-android-arm64": "4.39.0", + "@rollup/rollup-darwin-arm64": "4.39.0", + "@rollup/rollup-darwin-x64": "4.39.0", + "@rollup/rollup-freebsd-arm64": "4.39.0", + "@rollup/rollup-freebsd-x64": "4.39.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.39.0", + "@rollup/rollup-linux-arm-musleabihf": "4.39.0", + "@rollup/rollup-linux-arm64-gnu": "4.39.0", + "@rollup/rollup-linux-arm64-musl": "4.39.0", + "@rollup/rollup-linux-loongarch64-gnu": "4.39.0", + "@rollup/rollup-linux-powerpc64le-gnu": "4.39.0", + "@rollup/rollup-linux-riscv64-gnu": "4.39.0", + "@rollup/rollup-linux-riscv64-musl": "4.39.0", + "@rollup/rollup-linux-s390x-gnu": "4.39.0", + "@rollup/rollup-linux-x64-gnu": "4.39.0", + "@rollup/rollup-linux-x64-musl": "4.39.0", + "@rollup/rollup-win32-arm64-msvc": "4.39.0", + "@rollup/rollup-win32-ia32-msvc": "4.39.0", + "@rollup/rollup-win32-x64-msvc": "4.39.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz", + "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tailwindcss": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.3.tgz", + "integrity": "sha512-2Q+rw9vy1WFXu5cIxlvsabCwhU2qUwodGq03ODhLJ0jW4ek5BUtoCsnLB0qG+m8AHgEsSJcJGDSDe06FXlP74g==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", + "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "5.7.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", + "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.29.1", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.29.1.tgz", + "integrity": "sha512-f8cDkvndhbQMPcysk6CUSGBWV+g1utqdn71P5YKwMumVMOG/5k7cHq0KyG4O52nB0oKS4aN2Tp5+wB4APJGC+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.29.1", + "@typescript-eslint/parser": "8.29.1", + "@typescript-eslint/utils": "8.29.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/vite": { + "version": "6.2.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.2.5.tgz", + "integrity": "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "postcss": "^8.5.3", + "rollup": "^4.30.1" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "3.24.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.3.tgz", + "integrity": "sha512-HhY1oqzWCQWuUqvBFnsyrtZRhyPeR7SUGv+C4+MsisMuVfSPx8HpwWqH8tRahSlt6M3PiFAcoeFhZAqIXTxoSg==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/sona/week5/mission1/package.json b/sona/week5/mission1/package.json new file mode 100644 index 00000000..716199ac --- /dev/null +++ b/sona/week5/mission1/package.json @@ -0,0 +1,36 @@ +{ + "name": "ka", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "@hookform/resolvers": "^5.0.1", + "@tailwindcss/vite": "^4.1.3", + "axios": "^1.8.4", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "react-hook-form": "^7.55.0", + "react-router-dom": "^6.30.0", + "tailwindcss": "^4.1.3", + "zod": "^3.24.3" + }, + "devDependencies": { + "@eslint/js": "^9.21.0", + "@types/react": "^19.0.10", + "@types/react-dom": "^19.0.4", + "@vitejs/plugin-react-swc": "^3.8.0", + "eslint": "^9.21.0", + "eslint-plugin-react-hooks": "^5.1.0", + "eslint-plugin-react-refresh": "^0.4.19", + "globals": "^15.15.0", + "typescript": "~5.7.2", + "typescript-eslint": "^8.24.1", + "vite": "^6.2.0" + } +} diff --git a/sona/week5/mission1/public/eye.svg b/sona/week5/mission1/public/eye.svg new file mode 100644 index 00000000..1685de8e --- /dev/null +++ b/sona/week5/mission1/public/eye.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/sona/week5/mission1/public/eye2.svg b/sona/week5/mission1/public/eye2.svg new file mode 100644 index 00000000..b4eda2ee --- /dev/null +++ b/sona/week5/mission1/public/eye2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sona/week5/mission1/public/eyeHalf.svg b/sona/week5/mission1/public/eyeHalf.svg new file mode 100644 index 00000000..59cadacb --- /dev/null +++ b/sona/week5/mission1/public/eyeHalf.svg @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/sona/week5/mission1/public/eyeHalf2.svg b/sona/week5/mission1/public/eyeHalf2.svg new file mode 100644 index 00000000..f28a202b --- /dev/null +++ b/sona/week5/mission1/public/eyeHalf2.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/sona/week5/mission1/public/ggg-Photoroom.png b/sona/week5/mission1/public/ggg-Photoroom.png new file mode 100644 index 0000000000000000000000000000000000000000..52b7f4bdd5a03027b2b92f7dadcd7832fd9d2dc0 GIT binary patch literal 64341 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 YXXX8Xxg?`WG~h>FSw{)1U>*K{07S*Q-2eap literal 0 HcmV?d00001 diff --git a/sona/week5/mission1/public/profileImg.png b/sona/week5/mission1/public/profileImg.png new file mode 100644 index 0000000000000000000000000000000000000000..e9776efacaf8620c12e52876d233ce852588e659 GIT binary patch literal 1061 zcmex=rn zUe%mr@c#gVAP2)9h7-(;N(@YbjLd?J|Bo<80UgN92y!YMFfp^TFtP*Xv;-I#m>8Lu zxPS_vvW!g3EP{%KB1(qFj!uC^jT4Q65;qD1)r!ECz|8@P3c@rZL~k+hFaymMWENzw zXZTeBm%wCvQ%Wmq-FoTi_UU;EOG>`z?A!X==~m3vPw!8N^rxw*XI7wU(LLC7b9p=0 z@tno&GEYy6_D@@?EwUzT;}!?yZc#(`FuR&y1_l2hV& z(buM1@`ZRBZEidl^IY>s0oho-zr1G0%|C8l8aL1Epr1EbO6Jz9{6781;mpB-(hv5l zMIfs;HTk@ct0>~xh5I$d{!+UfKysq5)N{n2oGyR8ew)Se-Z{w18Rp4M?pBGii<+_} zZe!NDk2{UM=J$)$)qFc^ zQxRsZAb56C(UaN!hfoY-fzJ zQ~s{}u26Dk-_&1Dx8`j7oO{x)b@I36cjj`dzxChAZgb|$dwp43ZuPZw7q?9}sZ6T$ z&=A_H>wjvq%tHaVRg9OHUH0?a`Xl%+Ys7~8I$x99*X%CLG`!rq>+;{&h^`|pZWs9- zIy!n6fORk>shLfcESd5C*xty0`i#@3^M3S?vZy+=ZSNvoVPWYbJ4&rGk6&9ZRp0Oo zVgviu&aI1-w>g$g=l`3#=bz#2zZu6lZV4XsIPS5gLIvRpo8(M}^sEj48BX0k{@8Z@ zQQn*_JyT|#@7B6#y5-2X12!O)>|t%QpUgg=nt1Bs#qbwT8Q}_S? literal 0 HcmV?d00001 diff --git a/sona/week5/mission1/src/App.css b/sona/week5/mission1/src/App.css new file mode 100644 index 00000000..e69de29b diff --git a/sona/week5/mission1/src/App.tsx b/sona/week5/mission1/src/App.tsx new file mode 100644 index 00000000..96d867f6 --- /dev/null +++ b/sona/week5/mission1/src/App.tsx @@ -0,0 +1,16 @@ +import { RouterProvider } from "react-router-dom"; +import "./App.css"; +import router from "./routes"; +import { AuthProvider } from "./context/AuthContext"; + +function App() { + return ( + +
+ +
+
+ ); +} + +export default App; diff --git a/sona/week5/mission1/src/apis/auth.ts b/sona/week5/mission1/src/apis/auth.ts new file mode 100644 index 00000000..e1b268c5 --- /dev/null +++ b/sona/week5/mission1/src/apis/auth.ts @@ -0,0 +1,35 @@ +import { + RequesetSignupDto, + RequestSigninDto, + ResponseMyInfoDto, + ResponseSigninDto, + ResponseSignupDto, +} from "../types/auth"; +import axiosInstance from "./axios"; + +// 회원가입 +export const postSignup = async ( + body: RequesetSignupDto +): Promise => { + const { data } = await axiosInstance.post(`/v1/auth/signup`, body); + return data; +}; + +//로그인 +export const postSignin = async ( + body: RequestSigninDto +): Promise => { + const { data } = await axiosInstance.post("/v1/auth/signin", body); + return data; +}; + +//로그아웃 +export const postLogout = async () => { + const { data } = await axiosInstance.post("/v1/auth/signout"); + return data; +}; + +export const getmyInfo = async (): Promise => { + const { data } = await axiosInstance.get("/v1/users/me"); + return data; +}; diff --git a/sona/week5/mission1/src/apis/axios.ts b/sona/week5/mission1/src/apis/axios.ts new file mode 100644 index 00000000..6e8a0856 --- /dev/null +++ b/sona/week5/mission1/src/apis/axios.ts @@ -0,0 +1,118 @@ +import axios, { InternalAxiosRequestConfig } from "axios"; +import useLocalStorage from "../hooks/useLocalStorage"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; + +interface CustomInternalAxiosRequestConfig extends InternalAxiosRequestConfig { + _retry?: boolean; +} + +let refreshPromise: Promise | null = null; +const axiosInstance = axios.create({ + baseURL: import.meta.env.VITE_SERVER_API_URL, + headers: { + Authorization: `Bearer ${localStorage.getItem("accessToken")}`, + }, +}); +//요청 인터셉터 +//모든 요청 전에 accessToken을 Authorization 헤더에 추가한다. +axiosInstance.interceptors.request.use( + (config) => { + const { getItem } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); + const accesstoken = getItem(); + //accessToken이 존재하면 authorizatoin 헤더에 bearer토큰 형식으로 추가한다. + if (accesstoken) { + config.headers = config.headers || {}; + config.headers.Authorization = `Bearer ${accesstoken}`; + } + return config; + }, + //요청 인터셉터가 실패하면 에러 + (error) => Promise.reject(error) +); + +axiosInstance.interceptors.response.use( + (response) => response, + async (e) => { + const originalRequest: CustomInternalAxiosRequestConfig = e.config; + + if (e.response && e.response.status === 401 && !originalRequest._retry) { + originalRequest._retry = true; + + // refresh 실패 =>로그아웃, + if (originalRequest.url === "/v1/auth/refresh") { + const { removeItem: removeAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { removeItem: removeRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + removeAccessToken(); + removeRefreshToken(); + window.location.href = "/login"; + return Promise.reject(e); + } + + if (!refreshPromise) { + refreshPromise = (async () => { + const { getItem: getRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + const refreshToken = getRefreshToken(); + + if (!refreshToken) { + const { removeItem: removeAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { removeItem: removeRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + removeAccessToken(); + removeRefreshToken(); + window.location.href = "/login"; + throw new Error("No refresh token"); + } + + const { data } = await axiosInstance.post("/v1/auth/refresh", { + refresh: refreshToken, + }); + + const { setItem: setAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { setItem: setRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + setAccessToken(data.data.accessToken); + setRefreshToken(data.data.refreshToken); + + return data.data.accessToken; + })() + .catch((error) => { + const { removeItem: removeAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { removeItem: removeRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + removeAccessToken(); + removeRefreshToken(); + window.location.href = "/login"; + return Promise.reject(error); + }) + .finally(() => { + refreshPromise = null; + }); + } + + return refreshPromise.then((newAccessToken) => { + originalRequest.headers["Authorization"] = `Bearer${newAccessToken}`; + return axiosInstance.request(originalRequest); + }); + } + + return Promise.reject(e); + } +); + +export default axiosInstance; diff --git a/sona/week5/mission1/src/components/Header.tsx b/sona/week5/mission1/src/components/Header.tsx new file mode 100644 index 00000000..c2340420 --- /dev/null +++ b/sona/week5/mission1/src/components/Header.tsx @@ -0,0 +1,31 @@ +import { useMatch, useNavigate } from "react-router-dom"; + +export default function Header() { + const navigate = useNavigate(); + const signup = useMatch("/signup"); + const login = useMatch("/login"); + const my = useMatch("/my"); + const titles = [ + { match: login, title: "로그인" }, + { match: signup, title: "회원가입" }, + ]; + + const headerMatch = login || signup || my; + const getTitle = () => { + // match가 null이 아닌 것의 title을 반환, null이면 undefined + const matchedTitle = titles.find(({ match }) => match)?.title; + return matchedTitle; + }; + if (headerMatch) { + return ( + <> +
+

navigate(-1)}> + {"<"} +

+

{getTitle()}

+
+ + ); + } +} diff --git a/sona/week5/mission1/src/components/InputField.tsx b/sona/week5/mission1/src/components/InputField.tsx new file mode 100644 index 00000000..f2ea4047 --- /dev/null +++ b/sona/week5/mission1/src/components/InputField.tsx @@ -0,0 +1,33 @@ +type TInputField = { + placeholder?: string; + errorMsg?: string; + register?: object; + className?: string; + type?: string; +}; + +export default function InputField({ + placeholder = "", + errorMsg = "", + register, + className, + type, + ...props +}: TInputField) { + return ( + <> + + {errorMsg && ( +
+ {errorMsg} +
+ )} + + ); +} diff --git a/sona/week5/mission1/src/constants/key.ts b/sona/week5/mission1/src/constants/key.ts new file mode 100644 index 00000000..e3484972 --- /dev/null +++ b/sona/week5/mission1/src/constants/key.ts @@ -0,0 +1,4 @@ +export const LOCAL_STORAGE_KEY = { + accessToken: "accessToken", + refreshToken: "refreshToken", +}; diff --git a/sona/week5/mission1/src/context/AuthContext.tsx b/sona/week5/mission1/src/context/AuthContext.tsx new file mode 100644 index 00000000..bad2fdf6 --- /dev/null +++ b/sona/week5/mission1/src/context/AuthContext.tsx @@ -0,0 +1,88 @@ +import { createContext, PropsWithChildren, useContext, useState } from "react"; +import { RequestSigninDto } from "../types/auth"; +import useLocalStorage from "../hooks/useLocalStorage"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; +import { postLogout, postSignin } from "../apis/auth"; + +interface AuthContextType { + accessToken: string | null; + refreshToken: string | null; + login: (signinData: RequestSigninDto) => Promise; + logout: () => Promise; +} + +export const AuthContext = createContext({ + accessToken: null, + refreshToken: null, + login: async () => {}, + logout: async () => {}, +}); + +export const AuthProvider = ({ children }: PropsWithChildren) => { + const { + getItem: getAccessTokenFromStorage, + setItem: setAccessTokenInStorage, + removeItem: removeAccessTokenFromStorage, + } = useLocalStorage(LOCAL_STORAGE_KEY.accessToken); + const { + getItem: getRefreshTokenFromStorage, + setItem: setRefreshTokenInStorage, + removeItem: removeRefreshTokenFromStorage, + } = useLocalStorage(LOCAL_STORAGE_KEY.refreshToken); + + //지연초기화작업 + const [accessToken, setAccessToken] = useState( + getAccessTokenFromStorage() + ); + const [refreshToken, setRefreshToken] = useState( + getRefreshTokenFromStorage() + ); + + const login = async (signinData: RequestSigninDto) => { + try { + const { data } = await postSignin(signinData); + if (data) { + const newAccessToken = data.accessToken; + const newRefreshToken = data.refreshToken; + setAccessTokenInStorage(newAccessToken); + setRefreshTokenInStorage(newRefreshToken); + setAccessToken(newAccessToken); + setRefreshToken(newRefreshToken); + alert("로그인성공"); + } + } catch (error) { + console.log(error); + alert("로그인실패"); + throw error; + } + }; + + const logout = async () => { + try { + await postLogout(); + removeAccessTokenFromStorage(); + removeRefreshTokenFromStorage(); + + setAccessToken(null); + setRefreshToken(null); + alert("로그아웃에 성공"); + // localStorage.clear(); // 더 큰 규모에서는 localstorage에 많은 정보를 담아두기 때문에 조심해서 사용함 + } catch (e) { + console.log("로그아웃 실패함", e); + } + }; + + return ( + + {children} + + ); +}; + +export const useAuth = () => { + const context = useContext(AuthContext); + if (!context) { + throw new Error("AuthContext를 찾을 수 없습니다"); + } + return context; +}; diff --git a/sona/week5/mission1/src/hooks/useForm.tsx b/sona/week5/mission1/src/hooks/useForm.tsx new file mode 100644 index 00000000..b796bde8 --- /dev/null +++ b/sona/week5/mission1/src/hooks/useForm.tsx @@ -0,0 +1,50 @@ +import { useEffect, useState, ChangeEvent } from "react"; + +interface UseFormProps { + initialValue: T; + validate: (value: T) => Record; +} + +export default function useForm({ + initialValue, + validate, +}: UseFormProps) { + const [values, setValues] = useState(initialValue); + const [touched, setTouched] = useState>({}); + const [errors, setErrors] = useState>({}); + + const handleChange = (name: keyof T, text: string) => { + setValues((prev) => ({ + ...prev, + [name]: text, + })); + }; + + const handleBlur = (name: keyof T) => { + setTouched((prev) => ({ + ...prev, + [name]: true, + })); + }; + + const getInputProps = (name: keyof T) => { + const inputValue = values[name]; + + const onChange = ( + e: ChangeEvent + ) => { + handleChange(name, e.target.value); + }; + + const onBlur = () => handleBlur(name); + + return { value: inputValue, onChange, onBlur }; + }; + + useEffect(() => { + const newErrors = validate(values); + setErrors(newErrors); + }, [validate, values]); + + return { values, errors, touched, getInputProps }; +} diff --git a/sona/week5/mission1/src/hooks/useLocalStorage.ts b/sona/week5/mission1/src/hooks/useLocalStorage.ts new file mode 100644 index 00000000..a2f89588 --- /dev/null +++ b/sona/week5/mission1/src/hooks/useLocalStorage.ts @@ -0,0 +1,28 @@ +export const useLocalStorage = (key: string) => { + const setItem = (value: unknown) => { + try { + window.localStorage.setItem(key, JSON.stringify(value)); + } catch (error) { + console.log(error); + } + }; + + const getItem = () => { + try { + const item = window.localStorage.getItem(key); + return item ? JSON.parse(item) : null; + } catch (error) { + console.log(error); + } + }; + + const removeItem = () => { + try { + window.localStorage.removeItem(key); + } catch (error) { + console.log(error); + } + }; + return { setItem, getItem, removeItem }; +}; +export default useLocalStorage; diff --git a/sona/week5/mission1/src/index.css b/sona/week5/mission1/src/index.css new file mode 100644 index 00000000..691b83b7 --- /dev/null +++ b/sona/week5/mission1/src/index.css @@ -0,0 +1,10 @@ +@import "tailwindcss"; + +@layer components { + .inputField { + @apply w-full p-2 border border-gray-400 rounded-lg focus:outline-none text-sm mb-6; + } + .custom-btn { + @apply text-white bg-amber-600 px-2 rounded-2xl pb-1 h-7 text-sm; + } +} diff --git a/sona/week7/mission1/src/layout/HomePage.tsx b/sona/week5/mission1/src/layout/HomePage.tsx similarity index 100% rename from sona/week7/mission1/src/layout/HomePage.tsx rename to sona/week5/mission1/src/layout/HomePage.tsx diff --git a/sona/week5/mission1/src/layout/ProtectedLayout.tsx b/sona/week5/mission1/src/layout/ProtectedLayout.tsx new file mode 100644 index 00000000..d32e7373 --- /dev/null +++ b/sona/week5/mission1/src/layout/ProtectedLayout.tsx @@ -0,0 +1,14 @@ +import { Navigate, Outlet } from "react-router-dom"; +import { useAuth } from "../context/AuthContext"; + +const ProtectedLayout = () => { + //토큰 없으면 로그인페이지로 이동 + const { accessToken } = useAuth(); + + if (!accessToken) { + return ; + } + return ; +}; + +export default ProtectedLayout; diff --git a/sona/week5/mission1/src/main.tsx b/sona/week5/mission1/src/main.tsx new file mode 100644 index 00000000..bef5202a --- /dev/null +++ b/sona/week5/mission1/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/sona/week5/mission1/src/pages/GoogleLoginRedirectPage.tsx b/sona/week5/mission1/src/pages/GoogleLoginRedirectPage.tsx new file mode 100644 index 00000000..70fe1b58 --- /dev/null +++ b/sona/week5/mission1/src/pages/GoogleLoginRedirectPage.tsx @@ -0,0 +1,26 @@ +import { useEffect } from "react"; +import useLocalStorage from "../hooks/useLocalStorage"; +import { LOCAL_STORAGE_KEY } from "../constants/key"; + +export default function GoogleLoginRedirectPage() { + const { setItem: setAccessToken } = useLocalStorage( + LOCAL_STORAGE_KEY.accessToken + ); + const { setItem: setRefreshToken } = useLocalStorage( + LOCAL_STORAGE_KEY.refreshToken + ); + + useEffect(() => { + const urlParams = new URLSearchParams(window.location.search); + const accessToken = urlParams.get(LOCAL_STORAGE_KEY.accessToken); + const refreshToken = urlParams.get(LOCAL_STORAGE_KEY.refreshToken); + + if (accessToken) { + setAccessToken(accessToken); + setRefreshToken(refreshToken); + window.location.href = "/my"; + } + }, [setAccessToken, setRefreshToken]); + + return
rnrmf
; +} diff --git a/sona/week5/mission1/src/pages/Login.tsx b/sona/week5/mission1/src/pages/Login.tsx new file mode 100644 index 00000000..882e2b8e --- /dev/null +++ b/sona/week5/mission1/src/pages/Login.tsx @@ -0,0 +1,97 @@ +import useForm from "../hooks/useForm"; +import { UserSigninInformation, validateSignin } from "../utils/validate"; +import Header from "../components/Header"; +import { useNavigate } from "react-router-dom"; +import { useAuth } from "../context/AuthContext"; +import { useEffect } from "react"; + +export default function Login() { + const { login, accessToken } = useAuth(); + const navigate = useNavigate(); + + useEffect(() => { + if (accessToken) { + navigate("/"); + } + }, [navigate, accessToken]); + + const { values, errors, touched, getInputProps } = + useForm({ + initialValue: { + email: "", + password: "", + }, + validate: validateSignin, + }); + + const handleSubmit = async () => { + try { + await login(values); + navigate("/my"); + } catch (e) { + navigate("/"); + console.log(e); + } + }; + const handleGoogleLogin = () => { + window.location.href = + import.meta.env.VITE_SERVER_API_URL + "/v1/auth/google/login"; + }; + + return ( + <> +
+ +
+
+ OR +
+
+
+ + {errors.email && touched.email && ( +
+ {errors.email} +
+ )} +
+
+ + {errors.password && touched.password && ( +
+ {errors.password} +
+ )} +
+ + + ); +} diff --git a/sona/week5/mission1/src/pages/MyPage.tsx b/sona/week5/mission1/src/pages/MyPage.tsx new file mode 100644 index 00000000..0d379859 --- /dev/null +++ b/sona/week5/mission1/src/pages/MyPage.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from "react"; +import { getmyInfo } from "../apis/auth"; +import { ResponseMyInfoDto } from "../types/auth"; +import { useAuth } from "../context/AuthContext"; +import { useNavigate } from "react-router-dom"; + +const MyPage = () => { + const navigate = useNavigate(); + const { logout } = useAuth(); + const [data, setData] = useState([]); + useEffect(() => { + const getData = async () => { + const response = await getmyInfo(); + // console.log(response);header + setData(response); + }; + getData(); + }, []); + const handleLogout = async () => { + await logout(); + navigate("/login"); + }; + // console.log(data); + return ( + <> +
+
{data.data?.name}님 환영합니다
+ 프로필이미지 + +
+ + ); +}; + +export default MyPage; diff --git a/sona/week5/mission1/src/pages/Navbar.tsx b/sona/week5/mission1/src/pages/Navbar.tsx new file mode 100644 index 00000000..43292e4a --- /dev/null +++ b/sona/week5/mission1/src/pages/Navbar.tsx @@ -0,0 +1,19 @@ +import { Link } from "react-router-dom"; + +export default function NavBar() { + return ( + <> +
+ +
login
+ + +
sign up
+ + +
myPage
+ +
+ + ); +} diff --git a/sona/week5/mission1/src/pages/NotFoundPage.tsx b/sona/week5/mission1/src/pages/NotFoundPage.tsx new file mode 100644 index 00000000..fd64e89a --- /dev/null +++ b/sona/week5/mission1/src/pages/NotFoundPage.tsx @@ -0,0 +1,3 @@ +export default function NotFoundPage() { + return
찾을 수 없음
; +} diff --git a/sona/week5/mission1/src/pages/SignUp.tsx b/sona/week5/mission1/src/pages/SignUp.tsx new file mode 100644 index 00000000..654326c3 --- /dev/null +++ b/sona/week5/mission1/src/pages/SignUp.tsx @@ -0,0 +1,121 @@ +import { zodResolver } from "@hookform/resolvers/zod"; +import { SubmitHandler, useForm } from "react-hook-form"; +import { z } from "zod"; +import Header from "../components/Header"; +import InputField from "../components/InputField"; +import { postSignup } from "../apis/auth"; + +const schema = z + .object({ + email: z.string().email({ message: "올바른 이메일 형식이 아닙니다" }), + password: z + .string() + .min(8, { + message: "비밀번호는 8자 이상 이어야 합니다", + }) + .max(20, { message: "비밀번호는 20자 이상이어야합니다" }), + name: z.string().min(1, { message: " 이름을 입력해주세요" }), + passwordCheck: z + .string() + .min(8, { + message: "비밀번호는 8자 이상 이어야 합니다", + }) + .max(20, { message: "비밀번호는 20자 이상이어야합니다" }), + }) + .refine((data) => data.password === data.passwordCheck, { + message: "비밀번호가 일치하지 않습니다", + path: ["passwordCheck"], + }); + +type FormFields = z.infer; + +//isSubmitting은 로딩처리 +export default function SignUp() { + const { + formState: { errors, isSubmitting }, + register, + handleSubmit, + } = useForm({ + defaultValues: { + email: "", + password: "", + name: "", + passwordCheck: "", + }, + resolver: zodResolver(schema), //스키마 위반하면 error + mode: "onBlur", + }); + + const onSubmit: SubmitHandler = async (data) => { + const { passwordCheck, ...rest } = data; + const response = await postSignup(rest); + console.log(response); + console.log("응"); + }; + + return ( + <> +
+ +
+ +
구글 로그인
+
+
+
+ OR +
+
+
+ +
+
+ +
+
+ +
+ +
+ +
+ + + + ); +} diff --git a/sona/week5/mission1/src/routes.tsx b/sona/week5/mission1/src/routes.tsx new file mode 100644 index 00000000..f6f9e31e --- /dev/null +++ b/sona/week5/mission1/src/routes.tsx @@ -0,0 +1,39 @@ +import { createBrowserRouter, RouteObject } from "react-router-dom"; +import HomePage from "./layout/HomePage"; +import NotFoundPage from "./pages/NotFoundPage"; +import Login from "./pages/Login"; +import SignUp from "./pages/SignUp"; +import MyPage from "./pages/MyPage"; +import ProtectedLayout from "./layout/ProtectedLayout"; +import GoogleLoginRedirectPage from "./pages/GoogleLoginRedirectPage"; +//로그인 필요없는 페이지 +const publicRoutes: RouteObject[] = [ + { + path: "/", + element: , + errorElement: , + children: [ + { + path: "login", + element: , + }, + { + path: "signup", + element: , + }, + { path: "v1/auth/google/callback", element: }, + ], + }, +]; +//로그인 필요한 페이지 +const protectedRoutes: RouteObject[] = [ + { + path: "/", + element: , + errorElement: , + children: [{ path: "my", element: }], + }, +]; + +const router = createBrowserRouter([...publicRoutes, ...protectedRoutes]); +export default router; diff --git a/sona/week5/mission1/src/types/auth.ts b/sona/week5/mission1/src/types/auth.ts new file mode 100644 index 00000000..343e906c --- /dev/null +++ b/sona/week5/mission1/src/types/auth.ts @@ -0,0 +1,44 @@ +import { CommenResponse } from "./common"; + +//회원가입 관련 +export type RequesetSignupDto = { + name: string; + email: string; + bio?: string; + avatar?: string; + password: string; +}; + +export type ResponseSignupDto = CommenResponse<{ + id: number; + name: string; + email: string; + bio: boolean | null; + avatar: boolean | null; + createdAt: string; + updatedAt: string; +}>; + +//로그인관련 + +export type RequestSigninDto = CommenResponse<{ + email: string; + password: string; +}>; + +export type ResponseSigninDto = CommenResponse<{ + id: number; + name: string; + accessToken: string; + refreshToken: string; +}>; + +export type ResponseMyInfoDto = CommenResponse<{ + id: number; + name: string; + email: string; + bio: string | null; + avatar: string | null; + createdAt: Date; + updatedAt: Date; +}>; diff --git a/sona/week5/mission1/src/types/common.ts b/sona/week5/mission1/src/types/common.ts new file mode 100644 index 00000000..306b3dca --- /dev/null +++ b/sona/week5/mission1/src/types/common.ts @@ -0,0 +1,6 @@ +export type CommenResponse = { + status: boolean; + statusCode: number; + message: string; + data: T; +}; diff --git a/sona/week5/mission1/src/utils/validate.ts b/sona/week5/mission1/src/utils/validate.ts new file mode 100644 index 00000000..64dc40e8 --- /dev/null +++ b/sona/week5/mission1/src/utils/validate.ts @@ -0,0 +1,34 @@ +export type UserSigninInformation = { + email: string; + password: string; +}; + +export default function validateUser(values: UserSigninInformation) { + const errors = { + email: "", + password: "", + }; + + const emailValid = (email: string) => { + const emailRegx = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; + return emailRegx.test(email); + }; + + //숫자 +영문 조합8 + const passwordVaild = (password: string) => { + const passwordRegx = /^(?=.*[A-Za-z])(?=.*\d)[A-Za-z\d]{8,}$/; + return passwordRegx.test(password); + }; + + if (!emailValid(values.email)) { + errors.email = "올바른 이메일을 입력해주세요"; + } + if (!passwordVaild(values.password)) { + errors.password = "비밀번호는 영문,숫자 조합 8글자 이상으로 입력 해주세요"; + } + return errors; +} + +export function validateSignin(values: UserSigninInformation) { + return validateUser(values); +} diff --git a/sona/week5/mission1/src/vite-env.d.ts b/sona/week5/mission1/src/vite-env.d.ts new file mode 100644 index 00000000..12a29a12 --- /dev/null +++ b/sona/week5/mission1/src/vite-env.d.ts @@ -0,0 +1,9 @@ +/// + +interface ImportMetaEnv { + readonly VITE_SERVER_API_URL: string; +} + +interface ImportMeta { + readonly env: ImportMetaEnv; +} diff --git a/sona/week5/mission1/tsconfig.app.json b/sona/week5/mission1/tsconfig.app.json new file mode 100644 index 00000000..358ca9ba --- /dev/null +++ b/sona/week5/mission1/tsconfig.app.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2020", + "useDefineForClassFields": true, + "lib": ["ES2020", "DOM", "DOM.Iterable"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/sona/week5/mission1/tsconfig.json b/sona/week5/mission1/tsconfig.json new file mode 100644 index 00000000..1ffef600 --- /dev/null +++ b/sona/week5/mission1/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/sona/week5/mission1/tsconfig.node.json b/sona/week5/mission1/tsconfig.node.json new file mode 100644 index 00000000..db0becc8 --- /dev/null +++ b/sona/week5/mission1/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2022", + "lib": ["ES2023"], + "module": "ESNext", + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "isolatedModules": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/sona/week5/mission1/vite.config.ts b/sona/week5/mission1/vite.config.ts new file mode 100644 index 00000000..f961a459 --- /dev/null +++ b/sona/week5/mission1/vite.config.ts @@ -0,0 +1,8 @@ +import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react-swc"; +import tailwindcss from "@tailwindcss/vite"; + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], +}); diff --git a/sona/week7/mission1/index.html b/sona/week7/mission1/index.html index 420ff807..3a990580 100644 --- a/sona/week7/mission1/index.html +++ b/sona/week7/mission1/index.html @@ -4,7 +4,7 @@ - mission1 + week7-mission1
diff --git a/sona/week7/mission1/package-lock.json b/sona/week7/mission1/package-lock.json index f7fe679d..040f1ec4 100644 --- a/sona/week7/mission1/package-lock.json +++ b/sona/week7/mission1/package-lock.json @@ -10,10 +10,13 @@ "dependencies": { "@hookform/resolvers": "^5.0.1", "@tailwindcss/vite": "^4.1.3", + "@tanstack/react-query": "^5.75.5", + "@tanstack/react-query-devtools": "^5.75.5", "axios": "^1.8.4", "react": "^19.0.0", "react-dom": "^19.0.0", "react-hook-form": "^7.55.0", + "react-intersection-observer": "^9.16.0", "react-router-dom": "^6.30.0", "tailwindcss": "^4.1.3", "zod": "^3.24.3" @@ -1437,6 +1440,59 @@ "vite": "^5.2.0 || ^6" } }, + "node_modules/@tanstack/query-core": { + "version": "5.75.5", + "resolved": "https://registry.npmjs.org/@tanstack/query-core/-/query-core-5.75.5.tgz", + "integrity": "sha512-kPDOxtoMn2Ycycb76Givx2fi+2pzo98F9ifHL/NFiahEDpDwSVW6o12PRuQ0lQnBOunhRG5etatAhQij91M3MQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/query-devtools": { + "version": "5.74.7", + "resolved": "https://registry.npmjs.org/@tanstack/query-devtools/-/query-devtools-5.74.7.tgz", + "integrity": "sha512-nSNlfuGdnHf4yB0S+BoNYOE1o3oAH093weAYZolIHfS2stulyA/gWfSk/9H4ZFk5mAAHb5vNqAeJOmbdcGPEQw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, + "node_modules/@tanstack/react-query": { + "version": "5.75.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query/-/react-query-5.75.5.tgz", + "integrity": "sha512-QrLCJe40BgBVlWdAdf2ZEVJ0cISOuEy/HKupId1aTKU6gPJZVhSvZpH+Si7csRflCJphzlQ77Yx6gUxGW9o0XQ==", + "license": "MIT", + "dependencies": { + "@tanstack/query-core": "5.75.5" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^18 || ^19" + } + }, + "node_modules/@tanstack/react-query-devtools": { + "version": "5.75.5", + "resolved": "https://registry.npmjs.org/@tanstack/react-query-devtools/-/react-query-devtools-5.75.5.tgz", + "integrity": "sha512-S31U00nJOQIbxydRH1kOwdLRaLBrda8O5QjzmgkRg60UZzPGdbI6+873Qa0YGUfPeILDbR2ukgWyg7CJQPy4iA==", + "license": "MIT", + "dependencies": { + "@tanstack/query-devtools": "5.74.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "@tanstack/react-query": "^5.75.5", + "react": "^18 || ^19" + } + }, "node_modules/@types/estree": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.7.tgz", @@ -3275,6 +3331,21 @@ "react": "^16.8.0 || ^17 || ^18 || ^19" } }, + "node_modules/react-intersection-observer": { + "version": "9.16.0", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.16.0.tgz", + "integrity": "sha512-w9nJSEp+DrW9KmQmeWHQyfaP6b03v+TdXynaoA964Wxt7mdR3An11z4NNCQgL4gKSK7y1ver2Fq+JKH6CWEzUA==", + "license": "MIT", + "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 + } + } + }, "node_modules/react-router": { "version": "6.30.0", "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.30.0.tgz", diff --git a/sona/week7/mission1/package.json b/sona/week7/mission1/package.json index 716199ac..046a781b 100644 --- a/sona/week7/mission1/package.json +++ b/sona/week7/mission1/package.json @@ -12,10 +12,13 @@ "dependencies": { "@hookform/resolvers": "^5.0.1", "@tailwindcss/vite": "^4.1.3", + "@tanstack/react-query": "^5.75.5", + "@tanstack/react-query-devtools": "^5.75.5", "axios": "^1.8.4", "react": "^19.0.0", "react-dom": "^19.0.0", "react-hook-form": "^7.55.0", + "react-intersection-observer": "^9.16.0", "react-router-dom": "^6.30.0", "tailwindcss": "^4.1.3", "zod": "^3.24.3" diff --git a/sona/week7/mission1/public/detailHart.svg b/sona/week7/mission1/public/detailHart.svg new file mode 100644 index 00000000..7b0405e7 --- /dev/null +++ b/sona/week7/mission1/public/detailHart.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/sona/week7/mission1/public/hambugi.svg b/sona/week7/mission1/public/hambugi.svg new file mode 100644 index 00000000..ddcf7eaa --- /dev/null +++ b/sona/week7/mission1/public/hambugi.svg @@ -0,0 +1,3 @@ + + + diff --git a/sona/week7/mission1/public/hart.svg b/sona/week7/mission1/public/hart.svg new file mode 100644 index 00000000..d56aa45b --- /dev/null +++ b/sona/week7/mission1/public/hart.svg @@ -0,0 +1,3 @@ + + + diff --git a/sona/week7/mission1/public/search.svg b/sona/week7/mission1/public/search.svg new file mode 100644 index 00000000..64cdeae3 --- /dev/null +++ b/sona/week7/mission1/public/search.svg @@ -0,0 +1,3 @@ + + + diff --git a/sona/week7/mission1/src/App.tsx b/sona/week7/mission1/src/App.tsx index 96d867f6..23386e27 100644 --- a/sona/week7/mission1/src/App.tsx +++ b/sona/week7/mission1/src/App.tsx @@ -1,15 +1,25 @@ import { RouterProvider } from "react-router-dom"; import "./App.css"; import router from "./routes"; +import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import { AuthProvider } from "./context/AuthContext"; +// import { ReactQueryDevtools } from "@tanstack/react-query-devtools"; + +export const client = new QueryClient(); function App() { return ( - -
- -
-
+ + +
+ +
+
+ {/* {import.meta.env.DEV && } */} +
); } diff --git a/sona/week7/mission1/src/apis/comment.ts b/sona/week7/mission1/src/apis/comment.ts new file mode 100644 index 00000000..82c70aa7 --- /dev/null +++ b/sona/week7/mission1/src/apis/comment.ts @@ -0,0 +1,14 @@ +import { ResponseCommentListDto } from "../types/comment"; +import { CommentPageDto } from "../types/common"; +import axiosInstance from "./axios"; + +// getCommentList.ts +export const getCommentList = async ( + params: CommentPageDto +): Promise => { + const { lpId, ...queryParams } = params; + const { data } = await axiosInstance.get(`/v1/lps/${lpId}/comments`, { + params: queryParams, + }); + return data; +}; diff --git a/sona/week7/mission1/src/apis/lp.ts b/sona/week7/mission1/src/apis/lp.ts new file mode 100644 index 00000000..ab3b4e8d --- /dev/null +++ b/sona/week7/mission1/src/apis/lp.ts @@ -0,0 +1,13 @@ +import { PageDto } from "../types/common"; +import { ResponseLpListDto } from "../types/lp"; +import axiosInstance from "./axios"; + +// getLpList.ts +export const getLpList = async ( + pageDto: PageDto +): Promise => { + const { data } = await axiosInstance.get("/v1/lps", { + params: pageDto, + }); + return data; +}; diff --git a/sona/week7/mission1/src/components/Header.tsx b/sona/week7/mission1/src/components/Header.tsx index c2340420..55dc7973 100644 --- a/sona/week7/mission1/src/components/Header.tsx +++ b/sona/week7/mission1/src/components/Header.tsx @@ -20,9 +20,9 @@ export default function Header() { return ( <>
-

navigate(-1)}> +

{getTitle()}

diff --git a/sona/week7/mission1/src/constants/key.ts b/sona/week7/mission1/src/constants/key.ts index e3484972..c138675c 100644 --- a/sona/week7/mission1/src/constants/key.ts +++ b/sona/week7/mission1/src/constants/key.ts @@ -2,3 +2,10 @@ export const LOCAL_STORAGE_KEY = { accessToken: "accessToken", refreshToken: "refreshToken", }; + +export const QUERY_KEY = { + lps: "lps", + user: "user", + lpDetail: "LpDetail", + comment: "comment", +}; diff --git a/sona/week7/mission1/src/enums/common.ts b/sona/week7/mission1/src/enums/common.ts new file mode 100644 index 00000000..96a9c79c --- /dev/null +++ b/sona/week7/mission1/src/enums/common.ts @@ -0,0 +1,4 @@ +export enum PAGENATION_ORDER { + "asc" = "asc", + "desc" = "desc", +} diff --git a/sona/week7/mission1/src/hooks/useGetCommentList.tsx b/sona/week7/mission1/src/hooks/useGetCommentList.tsx new file mode 100644 index 00000000..d55b329f --- /dev/null +++ b/sona/week7/mission1/src/hooks/useGetCommentList.tsx @@ -0,0 +1,15 @@ +import { useQuery } from "@tanstack/react-query"; +import { QUERY_KEY } from "../constants/key"; +import { CommentPageDto } from "../types/common"; +import { getCommentList } from "../apis/comment"; + +export default function useGetCommentList(params: CommentPageDto) { + const { lpId } = params; + return useQuery({ + queryKey: [QUERY_KEY.comment, lpId], + queryFn: () => getCommentList(params), + select: (res) => res.data, + + enabled: !!lpId, + }); +} diff --git a/sona/week7/mission1/src/hooks/useGetLpList.ts b/sona/week7/mission1/src/hooks/useGetLpList.ts new file mode 100644 index 00000000..024e6677 --- /dev/null +++ b/sona/week7/mission1/src/hooks/useGetLpList.ts @@ -0,0 +1,30 @@ +import { useQuery } from "@tanstack/react-query"; +import { QUERY_KEY } from "../constants/key"; +import { getLpList } from "../apis/lp"; +import { PageDto } from "../types/common"; + +export default function useGetLpList({ + cursor, + search, + order, + limit, +}: PageDto) { + return useQuery({ + queryKey: [QUERY_KEY.lps, order], //순서 바뀔때 캐싱 + queryFn: () => + getLpList({ + cursor, + search, + order, + limit, + }), + select: (res) => { + return res.data; + }, + //이 시간 동안, 캐시된 데이터 그대로 사용 + // staleTime: 1000 * 60 * 5, + // 캐시된 데이터가 사라지기까지의 대기 시간, 예를들어 5*60*1000이면 데이터가 사라지기 까지의 대기시간 단위 (5분) + // gcTime: 100 * 60 * 10, + // enabled: + }); +} diff --git a/sona/week7/mission1/src/hooks/useGetProfile.tsx b/sona/week7/mission1/src/hooks/useGetProfile.tsx new file mode 100644 index 00000000..53771dd7 --- /dev/null +++ b/sona/week7/mission1/src/hooks/useGetProfile.tsx @@ -0,0 +1,16 @@ +import { useQuery } from "@tanstack/react-query"; +import axiosInstance from "../apis/axios"; +import { useAuth } from "../context/AuthContext"; +import { ResponseMyInfoDto } from "../types/auth"; +import { QUERY_KEY } from "../constants/key"; + +export default function useGetProfile() { + const { accessToken } = useAuth(); + const { data: user } = useQuery({ + queryKey: [QUERY_KEY.user], + queryFn: () => axiosInstance.get(`/v1/users/me`), + select: (res) => res.data.data, + enabled: !!accessToken, + }); + return { user }; +} diff --git a/sona/week7/mission1/src/hooks/usegetInfiniteCommentList.ts b/sona/week7/mission1/src/hooks/usegetInfiniteCommentList.ts new file mode 100644 index 00000000..6996a8f7 --- /dev/null +++ b/sona/week7/mission1/src/hooks/usegetInfiniteCommentList.ts @@ -0,0 +1,22 @@ +import { useInfiniteQuery } from "@tanstack/react-query"; +import { PAGENATION_ORDER } from "../enums/common"; +import { QUERY_KEY } from "../constants/key"; +import { ResponseCommentListDto } from "../types/comment"; +import { getCommentList } from "../apis/comment"; + +export default function useGetInfiniteCommentList( + lpId: number, + cursor: number, + limit: number, + order: PAGENATION_ORDER +) { + return useInfiniteQuery({ + queryKey: [QUERY_KEY.comment, order], + queryFn: ({ pageParam }) => + getCommentList({ lpId, cursor: pageParam, limit, order }), + initialPageParam: 0, + getNextPageParam: (lastPage: ResponseCommentListDto) => { + return lastPage.data.hasNext ? lastPage.data.nextCursor : undefined; + }, + }); +} diff --git a/sona/week7/mission1/src/hooks/usegetInfiniteLpList.ts b/sona/week7/mission1/src/hooks/usegetInfiniteLpList.ts new file mode 100644 index 00000000..15b9e708 --- /dev/null +++ b/sona/week7/mission1/src/hooks/usegetInfiniteLpList.ts @@ -0,0 +1,21 @@ +import { useInfiniteQuery } from "@tanstack/react-query"; +import { PAGENATION_ORDER } from "../enums/common"; +import { getLpList } from "../apis/lp"; +import { QUERY_KEY } from "../constants/key"; +import { ResponseLpListDto } from "../types/lp"; + +export default function useGetInfiniteLpList( + limit: number, + search: string, + order: PAGENATION_ORDER +) { + return useInfiniteQuery({ + queryKey: [QUERY_KEY.lps, search, order], + queryFn: ({ pageParam }) => + getLpList({ cursor: pageParam, limit, search, order }), + initialPageParam: 0, + getNextPageParam: (lastPage: ResponseLpListDto) => { + return lastPage.data.hasNext ? lastPage.data.nextCursor : undefined; + }, + }); +} diff --git a/sona/week7/mission1/src/index.css b/sona/week7/mission1/src/index.css index 691b83b7..d670c7a4 100644 --- a/sona/week7/mission1/src/index.css +++ b/sona/week7/mission1/src/index.css @@ -7,4 +7,13 @@ .custom-btn { @apply text-white bg-amber-600 px-2 rounded-2xl pb-1 h-7 text-sm; } + .account { + @apply flex flex-col items-center justify-center m-auto px-2; + } + .clickBtn { + @apply bg-white border-1 border-gray-700 p-1 rounded-sm text-black; + } + .noneClickBtn { + @apply border-1 border-gray-700 p-1 rounded-sm text-white; + } } diff --git a/sona/week7/mission1/src/layout/HomeLayout.tsx b/sona/week7/mission1/src/layout/HomeLayout.tsx new file mode 100644 index 00000000..9e1c8097 --- /dev/null +++ b/sona/week7/mission1/src/layout/HomeLayout.tsx @@ -0,0 +1,13 @@ +import { Outlet } from "react-router-dom"; +import NavBar from "../pages/Navbar"; + +export default function Layout() { + return ( + <> + +
+ +
+ + ); +} diff --git a/sona/week7/mission1/src/layout/ProtectedLayout.tsx b/sona/week7/mission1/src/layout/ProtectedLayout.tsx index d32e7373..1d4e050b 100644 --- a/sona/week7/mission1/src/layout/ProtectedLayout.tsx +++ b/sona/week7/mission1/src/layout/ProtectedLayout.tsx @@ -1,14 +1,21 @@ import { Navigate, Outlet } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; +import NavBar from "../pages/Navbar"; const ProtectedLayout = () => { //토큰 없으면 로그인페이지로 이동 const { accessToken } = useAuth(); if (!accessToken) { + alert("로그인이 필요합니다.로그인 해주세요!"); return ; } - return ; + return ( + <> + + + + ); }; export default ProtectedLayout; diff --git a/sona/week7/mission1/src/main.tsx b/sona/week7/mission1/src/main.tsx index bef5202a..10ed13e0 100644 --- a/sona/week7/mission1/src/main.tsx +++ b/sona/week7/mission1/src/main.tsx @@ -1,10 +1,10 @@ -import { StrictMode } from 'react' -import { createRoot } from 'react-dom/client' -import './index.css' -import App from './App.tsx' +import { StrictMode } from "react"; +import { createRoot } from "react-dom/client"; +import "./index.css"; +import App from "./App.tsx"; -createRoot(document.getElementById('root')!).render( +createRoot(document.getElementById("root")!).render( - , -) + +); diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx new file mode 100644 index 00000000..3f5b5c9b --- /dev/null +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -0,0 +1,68 @@ +import { useQuery } from "@tanstack/react-query"; +import axiosInstance from "../apis/axios"; +import { useParams } from "react-router-dom"; +import useGetProfile from "../hooks/useGetProfile"; +import getTimePassed from "../utils/dateCalculate"; +import { QUERY_KEY } from "../constants/key"; +import Comment from "./Comment"; + +export default function CardDetail() { + const { id } = useParams(); + // console.log(id); + const { user } = useGetProfile(); + + const { data } = useQuery({ + queryKey: [QUERY_KEY.lpDetail], + queryFn: () => axiosInstance.get(`v1/lps/${id}`), + select: (res) => res.data.data, + }); + + // console.log(data); + return ( + <> +
+
+
+
{user?.name}
+

+ {data?.createdAt + ? getTimePassed(new Date(data.createdAt)) + : "1일 전"} +

+
+ +
{data?.author?.name}
+
+
+ +
+
+
+

{data?.content}

+
+
    +
  • #sdas
  • +
  • #sdas
  • +
  • #sdas
  • +
  • #sdas
  • +
  • #sdas
  • +
  • #sdas
  • +
  • #sdas
  • +
  • #sdas
  • + {/* {data.tags?.map((tag) => { + return
  • #{tag.name}
  • ; + })} */} +
+
+ +

{data?.likes.length}

+
+ +
+ + ); +} diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx new file mode 100644 index 00000000..d3276e3b --- /dev/null +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -0,0 +1,92 @@ +import { useForm } from "react-hook-form"; +import InputField from "../components/InputField"; +import { useParams } from "react-router-dom"; +import CommentItem from "./CommentItem"; +import useGetInfiniteCommentList from "../hooks/usegetInfiniteCommentList"; +import { useInView } from "react-intersection-observer"; +import { useEffect } from "react"; +import CommentSkeleton from "./CommentSkeleton"; + +type FormFields = { + comment: string; +}; + +export default function Comment() { + const { id } = useParams(); + const lpId = Number(id); + // const { data: comment, isLoading, isError } = useGetCommentList({ lpId }); + const { data, isLoading, isError, hasNextPage, fetchNextPage, isFetching } = + useGetInfiniteCommentList(lpId, "", 10, "desc"); // limit 10개씩 + + // console.log(data); + const { + formState: { errors }, + register, + handleSubmit, + } = useForm({ + defaultValues: { + comment: "", + }, + }); + + const { ref, inView } = useInView({ threshold: 0 }); + + console.log(data); + useEffect(() => { + // console.log("inView:", inView, "hasNext:", hasNextPage); + if (inView && hasNextPage && !isFetching) { + fetchNextPage(); + } + }, [inView, hasNextPage, isFetching, fetchNextPage]); + + if (isLoading) return

로딩 중...

; + if (isError) return

에러가 발생했습니다.

; + + const allComments = data?.pages.flatMap((page) => page.data.data) ?? []; + + return ( +
+

댓글

+ + {/* 입력창 */} +
+ + +
+ + {/* 댓글 목록 */} +
+ {allComments.map((item) => ( + + ))} + + {/* 무한스크롤 감지 div */} +
+ + {isFetching && + Array.from({ length: 4 }).map((_, i) => ( + + ))} +
+
+ ); +} diff --git a/sona/week7/mission1/src/pages/CommentItem.tsx b/sona/week7/mission1/src/pages/CommentItem.tsx new file mode 100644 index 00000000..e985eede --- /dev/null +++ b/sona/week7/mission1/src/pages/CommentItem.tsx @@ -0,0 +1,28 @@ +import { Comment } from "../types/comment"; +interface CommentProps { + comment: Comment; +} + +export default function CommentItem({ comment }: CommentProps) { + // console.log(comment); + + return ( + <> +
+
+ 아바타 +
+
+

{comment.author.name}

+

{comment.content}

+
+
+ + ); +} diff --git a/sona/week7/mission1/src/pages/CommentSkeleton.tsx b/sona/week7/mission1/src/pages/CommentSkeleton.tsx new file mode 100644 index 00000000..97258195 --- /dev/null +++ b/sona/week7/mission1/src/pages/CommentSkeleton.tsx @@ -0,0 +1,14 @@ +export default function CommentSkeleton() { + return ( +
+
+
+
+ {/* 텍스트 */} +
+
+
+
+
+ ); +} diff --git a/sona/week7/mission1/src/pages/Home.tsx b/sona/week7/mission1/src/pages/Home.tsx new file mode 100644 index 00000000..ecc2d9f9 --- /dev/null +++ b/sona/week7/mission1/src/pages/Home.tsx @@ -0,0 +1,72 @@ +import { useEffect, useState } from "react"; +import { useInView } from "react-intersection-observer"; +import useGetInfiniteLpList from "../hooks/usegetInfiniteLpList"; +import { PAGENATION_ORDER } from "../enums/common"; +import { ResponseLpListDto } from "../types/lp"; +import LpCard from "./LpCard"; +import LpCardSkeleton from "./LpCardSkeleton"; + +export default function Home() { + const [sortOrder, setSortOrder] = useState( + PAGENATION_ORDER.desc + ); + + const { data, isFetching, isPending, isError, hasNextPage, fetchNextPage } = + useGetInfiniteLpList(5, "", sortOrder); + + // console.log(data); + + const { ref, inView } = useInView({ threshold: 0 }); + + useEffect(() => { + if (inView && !isFetching && hasNextPage) { + fetchNextPage(); + } + }, [inView, isFetching, hasNextPage, fetchNextPage]); + + // console.log(data?.pages.map((page: ResponseLpListDto) => page.data.data)); + + if (isPending) return
로딩 중...
; + if (isError) return
에러 발생
; + + const allLps = + data?.pages.flatMap((page: ResponseLpListDto) => page.data.data) ?? []; + + return ( +
+
+ + +
+ +
+ {allLps.map((item) => ( + + ))} + + {/* 로딩 중일 때 스켈레톤 */} + {isFetching && + Array.from({ length: 6 }).map((_, i) => ( + + ))} +
+ + {/* 무한스크롤 */} +
+
+ ); +} diff --git a/sona/week7/mission1/src/pages/Login.tsx b/sona/week7/mission1/src/pages/Login.tsx index 882e2b8e..1ea4137e 100644 --- a/sona/week7/mission1/src/pages/Login.tsx +++ b/sona/week7/mission1/src/pages/Login.tsx @@ -40,58 +40,62 @@ export default function Login() { return ( <> -
- -
-
- OR -
+
+
+ +
+
+ OR +
+
+
+ + {errors.email && touched.email && ( +
+ {errors.email} +
+ )} +
+
+ + {errors.password && touched.password && ( +
+ {errors.password} +
+ )} +
+
-
- - {errors.email && touched.email && ( -
- {errors.email} -
- )} -
-
- - {errors.password && touched.password && ( -
- {errors.password} -
- )} -
- ); } diff --git a/sona/week7/mission1/src/pages/LpCard.tsx b/sona/week7/mission1/src/pages/LpCard.tsx new file mode 100644 index 00000000..cfbb1b32 --- /dev/null +++ b/sona/week7/mission1/src/pages/LpCard.tsx @@ -0,0 +1,40 @@ +import { Link } from "react-router-dom"; +import { Lp } from "../types/lp"; +import getTimePassed from "../utils/dateCalculate"; + +interface LpProps { + item: Lp; +} + +export default function LpCard({ item }: LpProps) { + // console.log(item.thumbnail); + // console.log(item); + return ( + <> +
+ + +
+
+

{item.title}

+

+ {getTimePassed(new Date(item.createdAt))} +

+
+
+ +
{item.likes?.length || "0"}
+
+
+ +
+ + ); +} diff --git a/sona/week7/mission1/src/pages/LpCardSkeleton.tsx b/sona/week7/mission1/src/pages/LpCardSkeleton.tsx new file mode 100644 index 00000000..4a22e0ef --- /dev/null +++ b/sona/week7/mission1/src/pages/LpCardSkeleton.tsx @@ -0,0 +1,19 @@ +export default function LpCardSkeleton() { + return ( +
+
+ +
+
+
+
+
+ +
+
+
+
+
+
+ ); +} diff --git a/sona/week7/mission1/src/pages/Navbar.tsx b/sona/week7/mission1/src/pages/Navbar.tsx index 43292e4a..3fa6b370 100644 --- a/sona/week7/mission1/src/pages/Navbar.tsx +++ b/sona/week7/mission1/src/pages/Navbar.tsx @@ -1,18 +1,91 @@ +import { useState } from "react"; import { Link } from "react-router-dom"; +import { useAuth } from "../context/AuthContext"; +import useGetProfile from "../hooks/useGetProfile"; export default function NavBar() { + const [isOpen, setIsOpen] = useState(false); //여닫이 + + // const [isTrue, setISTrue] = useState(false); + + const { accessToken, logout } = useAuth(); + // if (accessToken) { + // setISTrue((item) => !item); + // } + const { user } = useGetProfile(); + // console.log(user); return ( <> -
- -
login
- - -
sign up
- - -
myPage
- + + + {/* 사이드바 */} + + {isOpen && ( +
setIsOpen(false)} + /> + )} + + {/* 사이드바 */} +
+
+ 메뉴 + +
+
    +
  • + setIsOpen(false)} + > + + 검색 + +
  • +
  • + 마이페이지 +
  • +
); diff --git a/sona/week7/mission1/src/pages/SignUp.tsx b/sona/week7/mission1/src/pages/SignUp.tsx index 654326c3..64fcccff 100644 --- a/sona/week7/mission1/src/pages/SignUp.tsx +++ b/sona/week7/mission1/src/pages/SignUp.tsx @@ -55,67 +55,68 @@ export default function SignUp() { return ( <> -
+
+
+
+ +
구글 로그인
+
+
+
+ OR +
+
+
+ +
+
+ +
+
+ +
-
- -
구글 로그인
-
-
-
- OR -
-
-
- -
-
- -
-
- -
+
+ +
-
- +
- - ); } diff --git a/sona/week7/mission1/src/routes.tsx b/sona/week7/mission1/src/routes.tsx index f6f9e31e..00ede0e8 100644 --- a/sona/week7/mission1/src/routes.tsx +++ b/sona/week7/mission1/src/routes.tsx @@ -1,18 +1,25 @@ import { createBrowserRouter, RouteObject } from "react-router-dom"; -import HomePage from "./layout/HomePage"; import NotFoundPage from "./pages/NotFoundPage"; import Login from "./pages/Login"; import SignUp from "./pages/SignUp"; import MyPage from "./pages/MyPage"; import ProtectedLayout from "./layout/ProtectedLayout"; import GoogleLoginRedirectPage from "./pages/GoogleLoginRedirectPage"; +import Layout from "./layout/HomeLayout"; +import Home from "./pages/Home"; +import CardDetail from "./pages/CardDetail"; +import Comment from "./pages/Comment"; //로그인 필요없는 페이지 const publicRoutes: RouteObject[] = [ { path: "/", - element: , + element: , errorElement: , children: [ + { + index: true, + element: , + }, { path: "login", element: , @@ -31,7 +38,17 @@ const protectedRoutes: RouteObject[] = [ path: "/", element: , errorElement: , - children: [{ path: "my", element: }], + children: [ + { path: "my", element: }, + { + path: "lp/:id", + element: , + }, + { + path: "lp/:id/comment", + element: , + }, + ], }, ]; diff --git a/sona/week7/mission1/src/types/comment.ts b/sona/week7/mission1/src/types/comment.ts new file mode 100644 index 00000000..db782648 --- /dev/null +++ b/sona/week7/mission1/src/types/comment.ts @@ -0,0 +1,23 @@ +import { CursorBasedResponse } from "./common"; + +export type Author = { + id: number; + name: string; + email: string; + bio: null; + avatar: null; + createdAt: Date; + updatedAt: Date; +}; + +export type Comment = { + id: number; + content: string; + lpId: number; + authorId: number; + createdAt: Date; + updatedAt: Date; + author: Author; +}; + +export type ResponseCommentListDto = CursorBasedResponse; diff --git a/sona/week7/mission1/src/types/common.ts b/sona/week7/mission1/src/types/common.ts index 306b3dca..1222285b 100644 --- a/sona/week7/mission1/src/types/common.ts +++ b/sona/week7/mission1/src/types/common.ts @@ -1,6 +1,30 @@ +import { PAGENATION_ORDER } from "../enums/common"; + export type CommenResponse = { status: boolean; statusCode: number; message: string; data: T; }; + +export type CursorBasedResponse = CommenResponse<{ + data: T; + nextCursor: number | null; + hasNext: boolean; +}>; + +//lp목록 조회 파라미터 +export type PageDto = { + cursor?: number; + limit?: number; + search?: string; + order?: PAGENATION_ORDER; +}; + +//comment 목록 조회 파라미터 +export type CommentPageDto = { + lpId: number; + cursor?: number; + limit?: number; + order?: PAGENATION_ORDER; +}; diff --git a/sona/week7/mission1/src/types/lp.ts b/sona/week7/mission1/src/types/lp.ts new file mode 100644 index 00000000..2854d11e --- /dev/null +++ b/sona/week7/mission1/src/types/lp.ts @@ -0,0 +1,27 @@ +import { CursorBasedResponse } from "./common"; + +export type Tag = { + id: number; + name: string; +}; + +export type Likes = { + id: number; + userId: number; + lpId: number; +}; + +export type Lp = { + id: number; + title: string; + content: string; + thumbnail: string; + published: boolean; + authorId: number; + createdAt: Date; + updatedAt: Date; + tags: Tag[]; + likes: Likes[]; +}; + +export type ResponseLpListDto = CursorBasedResponse; diff --git a/sona/week7/mission1/src/utils/dateCalculate.ts b/sona/week7/mission1/src/utils/dateCalculate.ts new file mode 100644 index 00000000..8a095e82 --- /dev/null +++ b/sona/week7/mission1/src/utils/dateCalculate.ts @@ -0,0 +1,21 @@ +export default function getTimePassed(createdAt: Date) { + // const formattedDate = createdAt.replace(/\./g, "-"); + const diff = new Date().getTime() - createdAt.getTime(); //ms변환 + + const sec = diff / 1000; //초 + const min = sec / 60; //분 + const hours = min / 60; //시간 + const days = hours / 24; //일 + + if (min < 0) { + return "방금 전"; + } else if (min < 60) { + return `${Math.round(min)}분 전`; + } else if (hours < 24) { + return `${Math.round(hours)}시간 전`; + } else if (days < 2) { + return "하루 전"; + } else { + return "며칠 전"; + } +} From 7760b3df3fde419db6576e9e481eac558d7e9563 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 01:46:37 +0900 Subject: [PATCH 02/28] =?UTF-8?q?=E2=9C=A8=20feat:=20lp=ED=8C=90=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/package-lock.json | 10 ++ sona/week7/mission1/package.json | 1 + sona/week7/mission1/public/lpImg.png | Bin 0 -> 90558 bytes sona/week7/mission1/public/whitePlus.svg | 1 + sona/week7/mission1/src/pages/CardDetail.tsx | 2 +- sona/week7/mission1/src/pages/Comment.tsx | 2 +- sona/week7/mission1/src/pages/Home.tsx | 118 ++++++++++++++----- sona/week7/mission1/src/pages/LpPlus.tsx | 7 ++ 8 files changed, 107 insertions(+), 34 deletions(-) create mode 100644 sona/week7/mission1/public/lpImg.png create mode 100644 sona/week7/mission1/public/whitePlus.svg create mode 100644 sona/week7/mission1/src/pages/LpPlus.tsx diff --git a/sona/week7/mission1/package-lock.json b/sona/week7/mission1/package-lock.json index 040f1ec4..b8a82620 100644 --- a/sona/week7/mission1/package-lock.json +++ b/sona/week7/mission1/package-lock.json @@ -13,6 +13,7 @@ "@tanstack/react-query": "^5.75.5", "@tanstack/react-query-devtools": "^5.75.5", "axios": "^1.8.4", + "lucide-react": "^0.510.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-hook-form": "^7.55.0", @@ -3018,6 +3019,15 @@ "dev": true, "license": "MIT" }, + "node_modules/lucide-react": { + "version": "0.510.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.510.0.tgz", + "integrity": "sha512-p8SQRAMVh7NhsAIETokSqDrc5CHnDLbV29mMnzaXx+Vc/hnqQzwI2r0FMWCcoTXnbw2KEjy48xwpGdEL+ck06Q==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", diff --git a/sona/week7/mission1/package.json b/sona/week7/mission1/package.json index 046a781b..ff796bbf 100644 --- a/sona/week7/mission1/package.json +++ b/sona/week7/mission1/package.json @@ -15,6 +15,7 @@ "@tanstack/react-query": "^5.75.5", "@tanstack/react-query-devtools": "^5.75.5", "axios": "^1.8.4", + "lucide-react": "^0.510.0", "react": "^19.0.0", "react-dom": "^19.0.0", "react-hook-form": "^7.55.0", diff --git a/sona/week7/mission1/public/lpImg.png b/sona/week7/mission1/public/lpImg.png new file mode 100644 index 0000000000000000000000000000000000000000..e35fb66f40c39f874feed78945cb5726d78d52d7 GIT binary patch literal 90558 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%0Aee)@?3$ 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 literal 0 HcmV?d00001 diff --git a/sona/week7/mission1/public/whitePlus.svg b/sona/week7/mission1/public/whitePlus.svg new file mode 100644 index 00000000..bb0cb50f --- /dev/null +++ b/sona/week7/mission1/public/whitePlus.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index 3f5b5c9b..a443082f 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -17,7 +17,7 @@ export default function CardDetail() { select: (res) => res.data.data, }); - // console.log(data); + console.log(data); return ( <>
diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx index d3276e3b..bcc02993 100644 --- a/sona/week7/mission1/src/pages/Comment.tsx +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -31,7 +31,7 @@ export default function Comment() { const { ref, inView } = useInView({ threshold: 0 }); - console.log(data); + // console.log(data); useEffect(() => { // console.log("inView:", inView, "hasNext:", hasNextPage); if (inView && hasNextPage && !isFetching) { diff --git a/sona/week7/mission1/src/pages/Home.tsx b/sona/week7/mission1/src/pages/Home.tsx index ecc2d9f9..6b82825e 100644 --- a/sona/week7/mission1/src/pages/Home.tsx +++ b/sona/week7/mission1/src/pages/Home.tsx @@ -5,12 +5,15 @@ import { PAGENATION_ORDER } from "../enums/common"; import { ResponseLpListDto } from "../types/lp"; import LpCard from "./LpCard"; import LpCardSkeleton from "./LpCardSkeleton"; +import InputField from "../components/InputField"; export default function Home() { const [sortOrder, setSortOrder] = useState( PAGENATION_ORDER.desc ); + const [isModalOpen, setIsModalOpen] = useState(false); + const { data, isFetching, isPending, isError, hasNextPage, fetchNextPage } = useGetInfiniteLpList(5, "", sortOrder); @@ -24,7 +27,7 @@ export default function Home() { } }, [inView, isFetching, hasNextPage, fetchNextPage]); - // console.log(data?.pages.map((page: ResponseLpListDto) => page.data.data)); + // const AddTag = () => {}; if (isPending) return
로딩 중...
; if (isError) return
에러 발생
; @@ -33,40 +36,91 @@ export default function Home() { data?.pages.flatMap((page: ResponseLpListDto) => page.data.data) ?? []; return ( -
-
- - -
+ <> +
+
+ + +
-
- {allLps.map((item) => ( - - ))} - - {/* 로딩 중일 때 스켈레톤 */} - {isFetching && - Array.from({ length: 6 }).map((_, i) => ( - +
+ {allLps.map((item) => ( + ))} + + {/* 로딩 중일 때 스켈레톤 */} + {isFetching && + Array.from({ length: 6 }).map((_, i) => ( + + ))} +
+ + {/* 무한스크롤 */} +
+
+ +
setIsModalOpen(true)} + > + 더하기
- {/* 무한스크롤 */} -
-
+ {isModalOpen && ( + <> +
setIsModalOpen(false)} + /> +
+
+ + + + +
+ + +
+ + +
+
+ + )} + ); } diff --git a/sona/week7/mission1/src/pages/LpPlus.tsx b/sona/week7/mission1/src/pages/LpPlus.tsx new file mode 100644 index 00000000..82001ba5 --- /dev/null +++ b/sona/week7/mission1/src/pages/LpPlus.tsx @@ -0,0 +1,7 @@ +export default function LpPlus() { + return ( + <> +
더하기
+ + ); +} From a175caed98ecc8f646289465b39a7971f64d35d6 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 01:46:55 +0900 Subject: [PATCH 03/28] =?UTF-8?q?=F0=9F=94=A5=20remove:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/LpPlus.tsx | 7 ------- 1 file changed, 7 deletions(-) delete mode 100644 sona/week7/mission1/src/pages/LpPlus.tsx diff --git a/sona/week7/mission1/src/pages/LpPlus.tsx b/sona/week7/mission1/src/pages/LpPlus.tsx deleted file mode 100644 index 82001ba5..00000000 --- a/sona/week7/mission1/src/pages/LpPlus.tsx +++ /dev/null @@ -1,7 +0,0 @@ -export default function LpPlus() { - return ( - <> -
더하기
- - ); -} From 43171c51ed595a2f2f46e9b7ad808cc8b9899675 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 02:27:33 +0900 Subject: [PATCH 04/28] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=95=B4=EC=8B=9C?= =?UTF-8?q?=ED=83=9C=EA=B7=B8=20=EC=B6=9C=EB=A0=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mission1/src/components/InputField.tsx | 6 +++ sona/week7/mission1/src/pages/Home.tsx | 45 ++++++++++++++----- sona/week7/mission1/src/pages/TagItem.tsx | 14 ++++++ 3 files changed, 53 insertions(+), 12 deletions(-) create mode 100644 sona/week7/mission1/src/pages/TagItem.tsx diff --git a/sona/week7/mission1/src/components/InputField.tsx b/sona/week7/mission1/src/components/InputField.tsx index f2ea4047..9923ab74 100644 --- a/sona/week7/mission1/src/components/InputField.tsx +++ b/sona/week7/mission1/src/components/InputField.tsx @@ -4,12 +4,16 @@ type TInputField = { register?: object; className?: string; type?: string; + onChange?: (e: React.ChangeEvent) => void; + value?: string; }; export default function InputField({ placeholder = "", errorMsg = "", register, + value, + onChange, className, type, ...props @@ -22,6 +26,8 @@ export default function InputField({ className={`inputField relative ${className}`} {...register} {...props} + value={value} + onChange={onChange} /> {errorMsg && (
diff --git a/sona/week7/mission1/src/pages/Home.tsx b/sona/week7/mission1/src/pages/Home.tsx index 6b82825e..dd63fdcb 100644 --- a/sona/week7/mission1/src/pages/Home.tsx +++ b/sona/week7/mission1/src/pages/Home.tsx @@ -6,13 +6,25 @@ import { ResponseLpListDto } from "../types/lp"; import LpCard from "./LpCard"; import LpCardSkeleton from "./LpCardSkeleton"; import InputField from "../components/InputField"; +import { TagItem } from "./TagItem"; export default function Home() { const [sortOrder, setSortOrder] = useState( PAGENATION_ORDER.desc ); - const [isModalOpen, setIsModalOpen] = useState(false); + const [isModalOpen, setIsModalOpen] = useState(false); //modal + + const [tags, setTags] = useState([]); + const [tagInputValue, setTagInputValue] = useState(""); + + const AddTag = () => { + const tagNode = tagInputValue.trim(); + if (tagNode) { + setTags([...tags, tagNode]); + } + setTagInputValue(""); + }; const { data, isFetching, isPending, isError, hasNextPage, fetchNextPage } = useGetInfiniteLpList(5, "", sortOrder); @@ -27,8 +39,6 @@ export default function Home() { } }, [inView, isFetching, hasNextPage, fetchNextPage]); - // const AddTag = () => {}; - if (isPending) return
로딩 중...
; if (isError) return
에러 발생
; @@ -90,8 +100,8 @@ export default function Home() { className="fixed inset-0 bg-black/50 z-5 " onClick={() => setIsModalOpen(false)} /> -
-
+
+
- -
diff --git a/sona/week7/mission1/src/pages/TagItem.tsx b/sona/week7/mission1/src/pages/TagItem.tsx new file mode 100644 index 00000000..a5771485 --- /dev/null +++ b/sona/week7/mission1/src/pages/TagItem.tsx @@ -0,0 +1,14 @@ +export function TagItem({ + tag, + onRemove, +}: { + tag: string; + onRemove: () => void; +}) { + return ( +
+
{tag}
+ +
+ ); +} From 1a85d01c739dccb9e1ab88f643988e3ef134b0e4 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 20:17:50 +0900 Subject: [PATCH 05/28] =?UTF-8?q?=E2=9C=A8=20feat:=20lp=EC=A0=84=EC=86=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/hooks/usePostLp.ts | 7 + sona/week7/mission1/src/pages/Home.tsx | 66 ++------ sona/week7/mission1/src/pages/LpModal.tsx | 184 +++++++++++++++++++++ sona/week7/mission1/src/types/lpPost.ts | 20 +++ 4 files changed, 221 insertions(+), 56 deletions(-) create mode 100644 sona/week7/mission1/src/hooks/usePostLp.ts create mode 100644 sona/week7/mission1/src/pages/LpModal.tsx create mode 100644 sona/week7/mission1/src/types/lpPost.ts diff --git a/sona/week7/mission1/src/hooks/usePostLp.ts b/sona/week7/mission1/src/hooks/usePostLp.ts new file mode 100644 index 00000000..8edf0a94 --- /dev/null +++ b/sona/week7/mission1/src/hooks/usePostLp.ts @@ -0,0 +1,7 @@ +// import { use } from "react"; + +// export default function usePostLp() { + +// return useMuta + +// } diff --git a/sona/week7/mission1/src/pages/Home.tsx b/sona/week7/mission1/src/pages/Home.tsx index dd63fdcb..6194c3de 100644 --- a/sona/week7/mission1/src/pages/Home.tsx +++ b/sona/week7/mission1/src/pages/Home.tsx @@ -5,8 +5,9 @@ import { PAGENATION_ORDER } from "../enums/common"; import { ResponseLpListDto } from "../types/lp"; import LpCard from "./LpCard"; import LpCardSkeleton from "./LpCardSkeleton"; -import InputField from "../components/InputField"; -import { TagItem } from "./TagItem"; +// import InputField from "../components/InputField"; +// import { TagItem } from "./TagItem"; +import LpModal from "./LpModal"; export default function Home() { const [sortOrder, setSortOrder] = useState( @@ -18,14 +19,6 @@ export default function Home() { const [tags, setTags] = useState([]); const [tagInputValue, setTagInputValue] = useState(""); - const AddTag = () => { - const tagNode = tagInputValue.trim(); - if (tagNode) { - setTags([...tags, tagNode]); - } - setTagInputValue(""); - }; - const { data, isFetching, isPending, isError, hasNextPage, fetchNextPage } = useGetInfiniteLpList(5, "", sortOrder); @@ -95,52 +88,13 @@ export default function Home() {
{isModalOpen && ( - <> -
setIsModalOpen(false)} - /> -
-
- - - - -
- setTagInputValue(e.target.value)} - value={tagInputValue} - /> - -
- {/* 태그출력 */} -
- {tags.map((item, i) => ( - setTags(tags.filter((t) => t !== item))} - /> - ))} -
- -
-
- + setIsModalOpen(false)} + setTagInputValue={setTagInputValue} + tagInputValue={tagInputValue} + tags={tags} + setTags={setTags} + /> )} ); diff --git a/sona/week7/mission1/src/pages/LpModal.tsx b/sona/week7/mission1/src/pages/LpModal.tsx new file mode 100644 index 00000000..d0b07025 --- /dev/null +++ b/sona/week7/mission1/src/pages/LpModal.tsx @@ -0,0 +1,184 @@ +import { useMutation } from "@tanstack/react-query"; +import InputField from "../components/InputField"; +import { TagItem } from "./TagItem"; +import axiosInstance from "../apis/axios"; +import { LpBodyPost, LpPost } from "../types/lpPost"; +import { useForm } from "react-hook-form"; +import { useRef, useState } from "react"; + +interface LpModalProps { + onClose: () => void; + tags: string[]; + setTags: (tags: string[]) => void; + tagInputValue: string; + setTagInputValue: (v: string) => void; +} + +interface UploadResponse { + status: boolean; + message: string; + statusCode: number; + data: { + imageUrl: string; + }; +} +export default function LpModal({ + onClose, + setTagInputValue, + tagInputValue, + tags, + setTags, +}: LpModalProps) { + const [preview, setPreview] = useState(null); + const fileInput = useRef(null); + + //추가 btn + const AddTag = () => { + const tagNode = tagInputValue.trim(); + if (tagNode) { + setTags([...tags, tagNode]); + } + setTagInputValue(""); + }; + + //useForm + const { + register, + handleSubmit, + formState: { errors }, + getValues, + } = useForm(); + + const addLp = useMutation({ + mutationFn: async () => { + const formValues = getValues(); + const postBody: LpBodyPost = { + title: formValues.lpName, + content: formValues.lpContent, + thumbnail: "", // 먼저 빈 값으로 초기화 + tags: tags, + published: true, // 임시로 true 처리 + }; + + // console.log("postB:", postBody); + // 1. 이미지 업로드 + if (fileInput.current) { + const imageFormData = new FormData(); + imageFormData.append("file", fileInput.current); + // console.log(imageFormData); + + const fileRes = await axiosInstance.post( + `/v1/uploads`, + imageFormData + ); // 경로 확인 필요 + console.log("f데잍", fileRes.data); + + const uploadedImagePath = fileRes?.data?.data?.imageUrl; + + if (!uploadedImagePath) { + throw new Error("이미지 업로드에 실패했습니다."); + } + + postBody.thumbnail = uploadedImagePath; + } + + // 2. LP 등록 요청 + return axiosInstance.post(`/v1/lps`, postBody); + }, + onSuccess: () => alert("추가가 완료 되었습니다."), + onError: (err) => console.error(err), + }); + + //이미지 바꾸기 + const imageChange = (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (file) { + setPreview(URL.createObjectURL(file)); + fileInput.current = file; + } + }; + + return ( + <> +
+
+
+ + + {/* lp누르면 이미지 삽입 */} +
addLp.mutate())}> + + + +
+ setTagInputValue(e.target.value)} + value={tagInputValue} + /> + +
+ {/* 태그출력 */} +
+ {tags.map((item, i) => ( + setTags(tags.filter((t) => t !== item))} + /> + ))} +
+ + +
+
+ + ); +} diff --git a/sona/week7/mission1/src/types/lpPost.ts b/sona/week7/mission1/src/types/lpPost.ts new file mode 100644 index 00000000..83ea8224 --- /dev/null +++ b/sona/week7/mission1/src/types/lpPost.ts @@ -0,0 +1,20 @@ +//Lp응답 type +export type LpPost = { + id: number; + title: string; + content: string; + thumbnail: string; + published: boolean; + authorId: number; + createdAt: Date; + updatedAt: Date; +}; + +//Lp body파라미터 +export type LpBodyPost = { + title: string; + content: string; + thumbnail: string; + tags: string[]; + published: boolean; +}; From 8359cb3db35c70d8715887bfdbcc165f79398f41 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 20:31:49 +0900 Subject: [PATCH 06/28] =?UTF-8?q?=E2=9C=A8=20feat:=20sort=20=EC=BB=B4?= =?UTF-8?q?=ED=8F=AC=EB=84=8C=ED=8A=B8=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CardDetail.tsx | 18 ++++------ sona/week7/mission1/src/pages/Comment.tsx | 15 +++++---- sona/week7/mission1/src/pages/Home.tsx | 20 ++--------- .../mission1/src/pages/SortComponent.tsx | 33 +++++++++++++++++++ 4 files changed, 51 insertions(+), 35 deletions(-) create mode 100644 sona/week7/mission1/src/pages/SortComponent.tsx diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index a443082f..47d01e70 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -45,17 +45,13 @@ export default function CardDetail() {

{data?.content}

    -
  • #sdas
  • -
  • #sdas
  • -
  • #sdas
  • -
  • #sdas
  • -
  • #sdas
  • -
  • #sdas
  • -
  • #sdas
  • -
  • #sdas
  • - {/* {data.tags?.map((tag) => { - return
  • #{tag.name}
  • ; - })} */} + {data.tags?.map((tag) => { + return ( +
  • + #{tag.name} +
  • + ); + })}
diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx index bcc02993..17e34dd1 100644 --- a/sona/week7/mission1/src/pages/Comment.tsx +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -4,19 +4,24 @@ import { useParams } from "react-router-dom"; import CommentItem from "./CommentItem"; import useGetInfiniteCommentList from "../hooks/usegetInfiniteCommentList"; import { useInView } from "react-intersection-observer"; -import { useEffect } from "react"; +import { useEffect, useState } from "react"; import CommentSkeleton from "./CommentSkeleton"; +import { PAGENATION_ORDER } from "../enums/common"; +import SortComponent from "./SortComponent"; type FormFields = { comment: string; }; export default function Comment() { + const [sortOrder, setSortOrder] = useState( + PAGENATION_ORDER.desc + ); const { id } = useParams(); const lpId = Number(id); // const { data: comment, isLoading, isError } = useGetCommentList({ lpId }); const { data, isLoading, isError, hasNextPage, fetchNextPage, isFetching } = - useGetInfiniteCommentList(lpId, "", 10, "desc"); // limit 10개씩 + useGetInfiniteCommentList(lpId, "", 10, sortOrder); // limit 10개씩 // console.log(data); const { @@ -48,6 +53,7 @@ export default function Comment() {

댓글

+ {/* 입력창 */}
-
diff --git a/sona/week7/mission1/src/pages/Home.tsx b/sona/week7/mission1/src/pages/Home.tsx index 6194c3de..564648c0 100644 --- a/sona/week7/mission1/src/pages/Home.tsx +++ b/sona/week7/mission1/src/pages/Home.tsx @@ -8,6 +8,7 @@ import LpCardSkeleton from "./LpCardSkeleton"; // import InputField from "../components/InputField"; // import { TagItem } from "./TagItem"; import LpModal from "./LpModal"; +import SortComponent from "./SortComponent"; export default function Home() { const [sortOrder, setSortOrder] = useState( @@ -41,24 +42,7 @@ export default function Home() { return ( <>
-
- - -
+
{allLps.map((item) => ( diff --git a/sona/week7/mission1/src/pages/SortComponent.tsx b/sona/week7/mission1/src/pages/SortComponent.tsx new file mode 100644 index 00000000..7b8e27f5 --- /dev/null +++ b/sona/week7/mission1/src/pages/SortComponent.tsx @@ -0,0 +1,33 @@ +// components/SortOrder.tsx +import { PAGENATION_ORDER } from "../enums/common"; + +interface SortOrderProps { + sortOrder: PAGENATION_ORDER; + setSortOrder: (order: PAGENATION_ORDER) => void; +} + +export default function SortComponent({ + sortOrder, + setSortOrder, +}: SortOrderProps) { + return ( +
+ + +
+ ); +} From 79d6b058690649a7ad996893b8b11e88a39c621d Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 21:14:56 +0900 Subject: [PATCH 07/28] option btn ui --- sona/week7/mission1/public/delete.svg | 1 + sona/week7/mission1/public/option.svg | 1 + sona/week7/mission1/public/pencil.svg | 1 + sona/week7/mission1/src/pages/CardDetail.tsx | 4 +-- sona/week7/mission1/src/pages/Comment.tsx | 9 ++++--- sona/week7/mission1/src/pages/CommentItem.tsx | 26 +++++++++++++++++-- 6 files changed, 34 insertions(+), 8 deletions(-) create mode 100644 sona/week7/mission1/public/delete.svg create mode 100644 sona/week7/mission1/public/option.svg create mode 100644 sona/week7/mission1/public/pencil.svg diff --git a/sona/week7/mission1/public/delete.svg b/sona/week7/mission1/public/delete.svg new file mode 100644 index 00000000..f179b40c --- /dev/null +++ b/sona/week7/mission1/public/delete.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sona/week7/mission1/public/option.svg b/sona/week7/mission1/public/option.svg new file mode 100644 index 00000000..42855261 --- /dev/null +++ b/sona/week7/mission1/public/option.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sona/week7/mission1/public/pencil.svg b/sona/week7/mission1/public/pencil.svg new file mode 100644 index 00000000..01a98220 --- /dev/null +++ b/sona/week7/mission1/public/pencil.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index 47d01e70..3ad0a7d9 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -12,7 +12,7 @@ export default function CardDetail() { const { user } = useGetProfile(); const { data } = useQuery({ - queryKey: [QUERY_KEY.lpDetail], + queryKey: [QUERY_KEY.lpDetail, id], queryFn: () => axiosInstance.get(`v1/lps/${id}`), select: (res) => res.data.data, }); @@ -45,7 +45,7 @@ export default function CardDetail() {

{data?.content}

    - {data.tags?.map((tag) => { + {data?.tags?.map((tag) => { return (
  • #{tag.name} diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx index 17e34dd1..20f4c39a 100644 --- a/sona/week7/mission1/src/pages/Comment.tsx +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -51,9 +51,10 @@ export default function Comment() { return (
    -

    댓글

    - - +
    +

    댓글

    + +
    {/* 입력창 */}
    -
    diff --git a/sona/week7/mission1/src/pages/CommentItem.tsx b/sona/week7/mission1/src/pages/CommentItem.tsx index e985eede..ca0c3aa6 100644 --- a/sona/week7/mission1/src/pages/CommentItem.tsx +++ b/sona/week7/mission1/src/pages/CommentItem.tsx @@ -1,3 +1,4 @@ +import { useState } from "react"; import { Comment } from "../types/comment"; interface CommentProps { comment: Comment; @@ -6,9 +7,11 @@ interface CommentProps { export default function CommentItem({ comment }: CommentProps) { // console.log(comment); + const [isOpenCModeal, setOpenCModal] = useState(false); + return ( <> -
    +
    -
    + {/* 내용 */} +

    {comment.author.name}

    {comment.content}

    + {/* 옵션버튼임 */} +
    + +
    + + {isOpenCModeal && ( +
    + + +
    + )}
    ); From dda72633725caa2e056d62216beee6f4edc13af5 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 21:51:54 +0900 Subject: [PATCH 08/28] =?UTF-8?q?=EB=8C=93=EA=B8=80=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/Comment.tsx | 28 +++++++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx index 20f4c39a..7a733ce4 100644 --- a/sona/week7/mission1/src/pages/Comment.tsx +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -8,9 +8,11 @@ import { useEffect, useState } from "react"; import CommentSkeleton from "./CommentSkeleton"; import { PAGENATION_ORDER } from "../enums/common"; import SortComponent from "./SortComponent"; +import { useMutation } from "@tanstack/react-query"; +import axiosInstance from "../apis/axios"; type FormFields = { - comment: string; + content: string; }; export default function Comment() { @@ -28,9 +30,22 @@ export default function Comment() { formState: { errors }, register, handleSubmit, + reset, } = useForm({ defaultValues: { - comment: "", + content: "", + }, + }); + + const addComment = useMutation({ + mutationFn: (comment) => + axiosInstance.post(`/v1/lps/${lpId}/comments`, comment), + onSuccess: () => { + alert("댓글작성 성공"); + reset(); + }, + onError: () => { + alert("댓글작성 실패"); }, }); @@ -61,9 +76,9 @@ export default function Comment() { type="text" placeholder="댓글을 입력하세요" className="mb-0" - errorMsg={errors.comment?.message} + errorMsg={errors.content?.message} register={{ - ...register("comment", { + ...register("content", { required: "댓글은 필수입력입니다", minLength: { value: 2, @@ -72,7 +87,10 @@ export default function Comment() { }), }} /> -
    From eb64766dc1d914f9cd9aa0686cb5b4add09dd26a Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 21:52:46 +0900 Subject: [PATCH 09/28] =?UTF-8?q?=EB=8C=93=EA=B8=80=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CommentItem.tsx | 28 +++++++++++++++++-- sona/week7/mission1/src/types/common.ts | 7 +++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/sona/week7/mission1/src/pages/CommentItem.tsx b/sona/week7/mission1/src/pages/CommentItem.tsx index ca0c3aa6..589f84d3 100644 --- a/sona/week7/mission1/src/pages/CommentItem.tsx +++ b/sona/week7/mission1/src/pages/CommentItem.tsx @@ -1,5 +1,8 @@ import { useState } from "react"; import { Comment } from "../types/comment"; +import { useMutation } from "@tanstack/react-query"; +import axiosInstance from "../apis/axios"; +import { LpDeleteDto } from "../types/common"; interface CommentProps { comment: Comment; } @@ -9,6 +12,18 @@ export default function CommentItem({ comment }: CommentProps) { const [isOpenCModeal, setOpenCModal] = useState(false); + const deleteBtn = useMutation({ + mutationFn: ({ commentId, lpId }: LpDeleteDto) => { + return axiosInstance.delete(`/v1/lps/${lpId}/comments/${commentId}`); + }, + onSuccess: () => { + alert("댓글이 삭제되었습니다"); + }, + onError: (err) => { + console.log(err); + }, + }); + return ( <>
    @@ -32,6 +47,7 @@ export default function CommentItem({ comment }: CommentProps) { className="" onClick={() => { setOpenCModal((pre) => !pre); + // console.log(comment.id); }} > 더보기 @@ -40,8 +56,16 @@ export default function CommentItem({ comment }: CommentProps) { {isOpenCModeal && (
    - - + +
    )}
    diff --git a/sona/week7/mission1/src/types/common.ts b/sona/week7/mission1/src/types/common.ts index 1222285b..6e57f588 100644 --- a/sona/week7/mission1/src/types/common.ts +++ b/sona/week7/mission1/src/types/common.ts @@ -1,3 +1,4 @@ +import { MutationMeta } from "@tanstack/react-query"; import { PAGENATION_ORDER } from "../enums/common"; export type CommenResponse = { @@ -28,3 +29,9 @@ export type CommentPageDto = { limit?: number; order?: PAGENATION_ORDER; }; + +//lp삭제 파라미터 +export type LpDeleteDto = { + lpId: number; + commentId: number; +}; From 090011040dd7393753029010d0256344232c1434 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 22:57:37 +0900 Subject: [PATCH 10/28] =?UTF-8?q?=E2=9C=A8=20feat:=20=EB=8C=93=EA=B8=80?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CommentItem.tsx | 89 +++++++++++++++++-- sona/week7/mission1/src/types/common.ts | 6 ++ 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/sona/week7/mission1/src/pages/CommentItem.tsx b/sona/week7/mission1/src/pages/CommentItem.tsx index 589f84d3..9228bee7 100644 --- a/sona/week7/mission1/src/pages/CommentItem.tsx +++ b/sona/week7/mission1/src/pages/CommentItem.tsx @@ -2,7 +2,9 @@ import { useState } from "react"; import { Comment } from "../types/comment"; import { useMutation } from "@tanstack/react-query"; import axiosInstance from "../apis/axios"; -import { LpDeleteDto } from "../types/common"; +import { CommentPatchDto, LpDeleteDto } from "../types/common"; +import InputField from "../components/InputField"; +import { useForm } from "react-hook-form"; interface CommentProps { comment: Comment; } @@ -12,6 +14,7 @@ export default function CommentItem({ comment }: CommentProps) { const [isOpenCModeal, setOpenCModal] = useState(false); + //삭제 const deleteBtn = useMutation({ mutationFn: ({ commentId, lpId }: LpDeleteDto) => { return axiosInstance.delete(`/v1/lps/${lpId}/comments/${commentId}`); @@ -24,6 +27,37 @@ export default function CommentItem({ comment }: CommentProps) { }, }); + //수정 + const editBtn = useMutation({ + mutationFn: ({ commentId, lpId, content }: CommentPatchDto) => { + return axiosInstance.patch(`v1/lps/${lpId}/comments/${commentId}`, { + content, + }); + }, + onSuccess: () => { + alert("댓글이 수정되었습니다"); + }, + onError: (err) => { + console.log(err); + }, + }); + + const [isEdit, setEdit] = useState(false); + // {isEdit?} + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + + const onEditSubmit = (data: { content: string }) => { + editBtn.mutate({ + commentId: comment.id, + lpId: comment.lpId, + content: data.content, + }); + }; + return ( <>
    @@ -37,10 +71,46 @@ export default function CommentItem({ comment }: CommentProps) { />
    {/* 내용 */} +
    -

    {comment.author.name}

    -

    {comment.content}

    +

    {comment.author.name}

    + + {isEdit ? ( +
    + + + + + ) : ( +

    {comment.content}

    + )}
    + {/* 옵션버튼임 */}
    - + {/* 옵션메뉴 */} {isOpenCModeal && (
    -
    )} diff --git a/sona/week7/mission1/src/types/common.ts b/sona/week7/mission1/src/types/common.ts index 6e57f588..26f431bb 100644 --- a/sona/week7/mission1/src/types/common.ts +++ b/sona/week7/mission1/src/types/common.ts @@ -35,3 +35,9 @@ export type LpDeleteDto = { lpId: number; commentId: number; }; +//commentDto +export type CommentPatchDto = { + lpId: number; + commentId: number; + content: string; +}; From fa21553034fc9acd0d4d99fb9bad46762647f38a Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 23:10:56 +0900 Subject: [PATCH 11/28] =?UTF-8?q?=E2=9C=A8=20feat:=20=ED=83=88=ED=87=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/Navbar.tsx | 31 +++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/sona/week7/mission1/src/pages/Navbar.tsx b/sona/week7/mission1/src/pages/Navbar.tsx index 3fa6b370..c29a9fa2 100644 --- a/sona/week7/mission1/src/pages/Navbar.tsx +++ b/sona/week7/mission1/src/pages/Navbar.tsx @@ -1,19 +1,36 @@ -import { useState } from "react"; -import { Link } from "react-router-dom"; +import { use, useState } from "react"; +import { Link, useNavigate } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; import useGetProfile from "../hooks/useGetProfile"; +import { useMutation } from "@tanstack/react-query"; +import axiosInstance from "../apis/axios"; export default function NavBar() { const [isOpen, setIsOpen] = useState(false); //여닫이 // const [isTrue, setISTrue] = useState(false); + const navigate = useNavigate(); const { accessToken, logout } = useAuth(); // if (accessToken) { // setISTrue((item) => !item); // } const { user } = useGetProfile(); // console.log(user); + + const deleteUser = useMutation({ + mutationFn: () => { + return axiosInstance.delete(`/v1/users`); + }, + onSuccess: () => { + alert("탈퇴 되었습니다"); + navigate("/"); + }, + onError: (err) => { + console.log(err); + }, + }); + return ( <>
    -
      +
      • 마이페이지
      • +
      • + +
From c17b45e84181cb9e5d23ec1935ee28470dcc58ad Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Wed, 14 May 2025 23:32:51 +0900 Subject: [PATCH 12/28] =?UTF-8?q?=F0=9F=9A=A7=20cont:=20:=20=ED=9A=8C?= =?UTF-8?q?=EC=9B=90=EA=B0=80=EC=9E=85=20refetch?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/Navbar.tsx | 45 ++++++++++++++++++++---- sona/week7/mission1/src/pages/SignUp.tsx | 5 ++- 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/sona/week7/mission1/src/pages/Navbar.tsx b/sona/week7/mission1/src/pages/Navbar.tsx index c29a9fa2..52c15c74 100644 --- a/sona/week7/mission1/src/pages/Navbar.tsx +++ b/sona/week7/mission1/src/pages/Navbar.tsx @@ -8,22 +8,21 @@ import axiosInstance from "../apis/axios"; export default function NavBar() { const [isOpen, setIsOpen] = useState(false); //여닫이 - // const [isTrue, setISTrue] = useState(false); - const navigate = useNavigate(); const { accessToken, logout } = useAuth(); - // if (accessToken) { - // setISTrue((item) => !item); - // } + const { user } = useGetProfile(); // console.log(user); + const [isDeleteUser, setDeleteUser] = useState(false); + const deleteUser = useMutation({ mutationFn: () => { return axiosInstance.delete(`/v1/users`); }, onSuccess: () => { alert("탈퇴 되었습니다"); + // logout(); //보류... navigate("/"); }, onError: (err) => { @@ -104,14 +103,46 @@ export default function NavBar() {
  • + {isDeleteUser && accessToken && ( +
    +
    + +

    + 정말 탈퇴하시겠습니까? +

    +
    + + +
    +
    +
    + )} ); } diff --git a/sona/week7/mission1/src/pages/SignUp.tsx b/sona/week7/mission1/src/pages/SignUp.tsx index 64fcccff..a4d9b756 100644 --- a/sona/week7/mission1/src/pages/SignUp.tsx +++ b/sona/week7/mission1/src/pages/SignUp.tsx @@ -4,6 +4,7 @@ import { z } from "zod"; import Header from "../components/Header"; import InputField from "../components/InputField"; import { postSignup } from "../apis/auth"; +import { useNavigate } from "react-router-dom"; const schema = z .object({ @@ -45,11 +46,13 @@ export default function SignUp() { resolver: zodResolver(schema), //스키마 위반하면 error mode: "onBlur", }); - + const navigate = useNavigate(); const onSubmit: SubmitHandler = async (data) => { const { passwordCheck, ...rest } = data; const response = await postSignup(rest); console.log(response); + alert("회원가입에 성공하셨습니다"); + navigate("/"); console.log("응"); }; From b3625ac67e18729bfa30e89ef39043b3acc58684 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Thu, 15 May 2025 19:54:04 +0900 Subject: [PATCH 13/28] =?UTF-8?q?=F0=9F=9A=A7=20cont:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../mission1/src/components/InputField.tsx | 10 +- sona/week7/mission1/src/pages/CardDetail.tsx | 20 +++- sona/week7/mission1/src/pages/Comment.tsx | 34 +++--- sona/week7/mission1/src/pages/CommentItem.tsx | 26 +++-- .../week7/mission1/src/pages/LpDetailEdit.tsx | 3 + sona/week7/mission1/src/pages/MyPage.tsx | 108 ++++++++++++++++-- sona/week7/mission1/src/types/common.ts | 7 ++ 7 files changed, 161 insertions(+), 47 deletions(-) create mode 100644 sona/week7/mission1/src/pages/LpDetailEdit.tsx diff --git a/sona/week7/mission1/src/components/InputField.tsx b/sona/week7/mission1/src/components/InputField.tsx index 9923ab74..f1f88c86 100644 --- a/sona/week7/mission1/src/components/InputField.tsx +++ b/sona/week7/mission1/src/components/InputField.tsx @@ -12,8 +12,8 @@ export default function InputField({ placeholder = "", errorMsg = "", register, - value, - onChange, + // value, + // onChange, className, type, ...props @@ -26,11 +26,11 @@ export default function InputField({ className={`inputField relative ${className}`} {...register} {...props} - value={value} - onChange={onChange} + // value={value} + // onChange={onChange} /> {errorMsg && ( -
    +
    {errorMsg}
    )} diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index 3ad0a7d9..53591b85 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -1,7 +1,7 @@ import { useQuery } from "@tanstack/react-query"; import axiosInstance from "../apis/axios"; import { useParams } from "react-router-dom"; -import useGetProfile from "../hooks/useGetProfile"; +// import useGetProfile from "../hooks/useGetProfile"; import getTimePassed from "../utils/dateCalculate"; import { QUERY_KEY } from "../constants/key"; import Comment from "./Comment"; @@ -9,7 +9,7 @@ import Comment from "./Comment"; export default function CardDetail() { const { id } = useParams(); // console.log(id); - const { user } = useGetProfile(); + // const { user } = useGetProfile(); const { data } = useQuery({ queryKey: [QUERY_KEY.lpDetail, id], @@ -21,9 +21,9 @@ export default function CardDetail() { return ( <>
    -
    +
    -
    {user?.name}
    +
    {data?.author?.name}

    {data?.createdAt ? getTimePassed(new Date(data.createdAt)) @@ -31,7 +31,17 @@ export default function CardDetail() {

    -
    {data?.author?.name}
    +
    {data?.title}
    + {/* 삭제 수정 */} +
    + + +
    +
    {/* 입력창 */}
    - +
    + +
    diff --git a/sona/week7/mission1/src/pages/CommentItem.tsx b/sona/week7/mission1/src/pages/CommentItem.tsx index 9228bee7..1ebd1e7f 100644 --- a/sona/week7/mission1/src/pages/CommentItem.tsx +++ b/sona/week7/mission1/src/pages/CommentItem.tsx @@ -80,18 +80,20 @@ export default function CommentItem({ comment }: CommentProps) { onSubmit={handleSubmit(onEditSubmit)} className="flex gap-2 mt-1" > - +
    + +
    +
    +

    {data.data?.email}

    +
    + + ) : ( +
    +

    {data.data?.name}

    +

    {data.data?.bio || "프론트 짱"}

    +

    {data.data?.email}

    +
    + )} +
    + +
    + + {/* tab */} +
    + + +
    diff --git a/sona/week7/mission1/src/types/common.ts b/sona/week7/mission1/src/types/common.ts index 26f431bb..16a4080f 100644 --- a/sona/week7/mission1/src/types/common.ts +++ b/sona/week7/mission1/src/types/common.ts @@ -41,3 +41,10 @@ export type CommentPatchDto = { commentId: number; content: string; }; + +//userEditDto +export type UserPatchDto = { + name: string; + bio?: string; + avatar?: string; +}; From a7d01525e2d0320fa5270676c6d465c4d3d59c1c Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Thu, 15 May 2025 20:06:48 +0900 Subject: [PATCH 14/28] =?UTF-8?q?=F0=9F=92=84=20style:=20modal=20=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/LpModal.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sona/week7/mission1/src/pages/LpModal.tsx b/sona/week7/mission1/src/pages/LpModal.tsx index d0b07025..da8c1af2 100644 --- a/sona/week7/mission1/src/pages/LpModal.tsx +++ b/sona/week7/mission1/src/pages/LpModal.tsx @@ -101,7 +101,7 @@ export default function LpModal({ return ( <>
    -
    +
    - -
    +
    {data?.title}
    + {/* 삭제 수정 */} + {user?.id === data?.author?.id && ( +
    + + + + +
    + )} -
    -
    - -
    +
    +
    + +
    +
    +

    {data?.content}

    -

    {data?.content}

    -
    -
      - {data?.tags?.map((tag) => { - return ( -
    • - #{tag.name} -
    • - ); - })} -
    +
      + {data?.tags?.map((tag) => { + return ( +
    • + #{tag.name} +
    • + ); + })} +
    +
    -

    {data?.likes.length}

    +

    {data?.likes?.length}

    diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx index 4aaf0c90..01c5c72d 100644 --- a/sona/week7/mission1/src/pages/Comment.tsx +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -67,11 +67,11 @@ export default function Comment() { return (
    -

    댓글

    +

    댓글

    {/* 입력창 */} -
    +
    { @@ -48,7 +54,7 @@ export default function CommentItem({ comment }: CommentProps) { register, handleSubmit, formState: { errors }, - } = useForm(); + } = useForm(); const onEditSubmit = (data: { content: string }) => { editBtn.mutate({ @@ -114,17 +120,19 @@ export default function CommentItem({ comment }: CommentProps) {
    {/* 옵션버튼임 */} -
    - -
    + {user?.id === comment?.authorId && ( +
    + +
    + )} {/* 옵션메뉴 */} {isOpenCModeal && (
    diff --git a/sona/week7/mission1/src/pages/Home.tsx b/sona/week7/mission1/src/pages/Home.tsx index 564648c0..52f9b32b 100644 --- a/sona/week7/mission1/src/pages/Home.tsx +++ b/sona/week7/mission1/src/pages/Home.tsx @@ -1,30 +1,23 @@ +// 생략된 import들은 그대로 유지 import { useEffect, useState } from "react"; -import { useInView } from "react-intersection-observer"; -import useGetInfiniteLpList from "../hooks/usegetInfiniteLpList"; -import { PAGENATION_ORDER } from "../enums/common"; import { ResponseLpListDto } from "../types/lp"; -import LpCard from "./LpCard"; import LpCardSkeleton from "./LpCardSkeleton"; -// import InputField from "../components/InputField"; -// import { TagItem } from "./TagItem"; import LpModal from "./LpModal"; import SortComponent from "./SortComponent"; +import useGetInfiniteLpList from "../hooks/usegetInfiniteLpList"; +import { useInView } from "react-intersection-observer"; +import LpCard from "./LpCard"; +import { PAGENATION_ORDER } from "../enums/common"; export default function Home() { + const [isModalOpen, setIsModalOpen] = useState(false); const [sortOrder, setSortOrder] = useState( PAGENATION_ORDER.desc ); - const [isModalOpen, setIsModalOpen] = useState(false); //modal - - const [tags, setTags] = useState([]); - const [tagInputValue, setTagInputValue] = useState(""); - const { data, isFetching, isPending, isError, hasNextPage, fetchNextPage } = useGetInfiniteLpList(5, "", sortOrder); - // console.log(data); - const { ref, inView } = useInView({ threshold: 0 }); useEffect(() => { @@ -33,9 +26,6 @@ export default function Home() { } }, [inView, isFetching, hasNextPage, fetchNextPage]); - if (isPending) return
    로딩 중...
    ; - if (isError) return
    에러 발생
    ; - const allLps = data?.pages.flatMap((page: ResponseLpListDto) => page.data.data) ?? []; @@ -48,36 +38,32 @@ export default function Home() { {allLps.map((item) => ( ))} - - {/* 로딩 중일 때 스켈레톤 */} {isFetching && - Array.from({ length: 6 }).map((_, i) => ( - - ))} + Array.from({ length: 6 }).map((_, i) => )}
    - {/* 무한스크롤 */}
    + {/* lp등록 */}
    setIsModalOpen(true)} > 더하기
    + {/* 등록 모달 */} {isModalOpen && ( setIsModalOpen(false)} - setTagInputValue={setTagInputValue} - tagInputValue={tagInputValue} - tags={tags} - setTags={setTags} + onSubmitSuccess={() => { + setIsModalOpen(false); + }} /> )} diff --git a/sona/week7/mission1/src/pages/LpDetailEdit.tsx b/sona/week7/mission1/src/pages/LpDetailEdit.tsx index b7c050fa..51736140 100644 --- a/sona/week7/mission1/src/pages/LpDetailEdit.tsx +++ b/sona/week7/mission1/src/pages/LpDetailEdit.tsx @@ -1,3 +1,45 @@ +import { useState } from "react"; +import { useParams, useNavigate } from "react-router-dom"; +import { useQuery } from "@tanstack/react-query"; +import axiosInstance from "../apis/axios"; +import { QUERY_KEY } from "../constants/key"; +import LpModal from "./LpModal"; +import { Tag } from "../types/lp"; + export default function LpDetailEdit() { - return; + const { id } = useParams(); + const lpId = Number(id); + const navigate = useNavigate(); + const [isModalOpen, setIsModalOpen] = useState(true); + + const { data, isLoading, isError } = useQuery({ + queryKey: [QUERY_KEY.lpDetail, lpId], + queryFn: () => axiosInstance.get(`/v1/lps/${lpId}`), + select: (res) => res.data.data, + enabled: !!lpId, + }); + + if (isLoading) return

    로딩 중...

    ; + if (isError || !data) + return

    에러 발생

    ; + + return ( + <> + {isModalOpen && ( + t.name), // tags가 [{ id, name }] 형태라면 + }} + onClose={() => navigate(-1)} + onSubmitSuccess={() => { + navigate(`/lps/${lpId}`); + }} + /> + )} + + ); } diff --git a/sona/week7/mission1/src/pages/LpModal.tsx b/sona/week7/mission1/src/pages/LpModal.tsx index da8c1af2..c2c95222 100644 --- a/sona/week7/mission1/src/pages/LpModal.tsx +++ b/sona/week7/mission1/src/pages/LpModal.tsx @@ -2,16 +2,20 @@ import { useMutation } from "@tanstack/react-query"; import InputField from "../components/InputField"; import { TagItem } from "./TagItem"; import axiosInstance from "../apis/axios"; -import { LpBodyPost, LpPost } from "../types/lpPost"; +import { LpBodyPost } from "../types/lpPost"; import { useForm } from "react-hook-form"; import { useRef, useState } from "react"; interface LpModalProps { onClose: () => void; - tags: string[]; - setTags: (tags: string[]) => void; - tagInputValue: string; - setTagInputValue: (v: string) => void; + onSubmitSuccess?: () => void; + lpId?: number; + initialData?: { + lpName: string; + lpContent: string; + thumbnail?: string; + tags: string[]; + }; } interface UploadResponse { @@ -24,72 +28,77 @@ interface UploadResponse { } export default function LpModal({ onClose, - setTagInputValue, - tagInputValue, - tags, - setTags, + onSubmitSuccess, + lpId, + initialData, }: LpModalProps) { - const [preview, setPreview] = useState(null); + const isEdit = !!lpId; + const [tagInputValue, setTagInputValue] = useState(""); + const [tags, setTags] = useState(initialData?.tags ?? []); + const [preview, setPreview] = useState( + initialData?.thumbnail ?? null + ); const fileInput = useRef(null); - //추가 btn const AddTag = () => { const tagNode = tagInputValue.trim(); - if (tagNode) { + if (tagNode && !tags.includes(tagNode)) { setTags([...tags, tagNode]); } setTagInputValue(""); }; - //useForm const { register, handleSubmit, formState: { errors }, getValues, - } = useForm(); - - const addLp = useMutation({ - mutationFn: async () => { - const formValues = getValues(); - const postBody: LpBodyPost = { - title: formValues.lpName, - content: formValues.lpContent, - thumbnail: "", // 먼저 빈 값으로 초기화 - tags: tags, - published: true, // 임시로 true 처리 - }; - - // console.log("postB:", postBody); - // 1. 이미지 업로드 - if (fileInput.current) { - const imageFormData = new FormData(); - imageFormData.append("file", fileInput.current); - // console.log(imageFormData); - - const fileRes = await axiosInstance.post( - `/v1/uploads`, - imageFormData - ); // 경로 확인 필요 - console.log("f데잍", fileRes.data); + } = useForm({ + defaultValues: { + lpName: initialData?.lpName ?? "", + lpContent: initialData?.lpContent ?? "", + }, + }); - const uploadedImagePath = fileRes?.data?.data?.imageUrl; + const mutationFn = async () => { + const formValues = getValues(); + const postBody: LpBodyPost = { + title: formValues.lpName, + content: formValues.lpContent, + thumbnail: "", + tags, + published: true, + }; - if (!uploadedImagePath) { - throw new Error("이미지 업로드에 실패했습니다."); - } + if (fileInput.current) { + const imageFormData = new FormData(); + imageFormData.append("file", fileInput.current); + const fileRes = await axiosInstance.post( + `/v1/uploads`, + imageFormData + ); + const uploadedImagePath = fileRes?.data?.data?.imageUrl; + if (!uploadedImagePath) throw new Error("이미지 업로드 실패"); + postBody.thumbnail = uploadedImagePath; + } else if (preview) { + postBody.thumbnail = preview; + } - postBody.thumbnail = uploadedImagePath; - } + return isEdit + ? axiosInstance.patch(`/v1/lps/${lpId}`, postBody) + : axiosInstance.post(`/v1/lps`, postBody); + }; - // 2. LP 등록 요청 - return axiosInstance.post(`/v1/lps`, postBody); + const addOrUpdateLp = useMutation({ + mutationFn, + onSuccess: () => { + alert(isEdit ? "수정 완료" : "추가 완료"); + onSubmitSuccess?.(); + onClose(); }, - onSuccess: () => alert("추가가 완료 되었습니다."), onError: (err) => console.error(err), }); - //이미지 바꾸기 const imageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { @@ -100,15 +109,20 @@ export default function LpModal({ return ( <> -
    -
    -
    - - {/* lp누르면 이미지 삽입 */} -
    addLp.mutate())}> + addOrUpdateLp.mutate())}> - - -
    +
    + +
    +
    + +
    +
    setTagInputValue(e.target.value)} + className="w-full mb-0" value={tagInputValue} + onChange={(e) => setTagInputValue(e.target.value)} />
    - {/* 태그출력 */} -
    - {tags.map((item, i) => ( + +
    + {tags.map((tag, i) => ( setTags(tags.filter((t) => t !== item))} + tag={tag} + onRemove={() => setTags(tags.filter((t) => t !== tag))} /> ))}
    -
    diff --git a/sona/week7/mission1/src/pages/MyPage.tsx b/sona/week7/mission1/src/pages/MyPage.tsx index e92983c1..2368ceba 100644 --- a/sona/week7/mission1/src/pages/MyPage.tsx +++ b/sona/week7/mission1/src/pages/MyPage.tsx @@ -9,6 +9,11 @@ import { useMutation } from "@tanstack/react-query"; import { UserPatchDto } from "../types/common"; import axiosInstance from "../apis/axios"; +interface MyPageForm { + name: string; + bio: string; +} + const MyPage = () => { const navigate = useNavigate(); const { logout } = useAuth(); @@ -31,12 +36,10 @@ const MyPage = () => { console.log(data); const { - isError, register, - reset, handleSubmit, formState: { errors }, - } = useForm(); + } = useForm(); const userEditBtn = useMutation({ mutationFn: (formData: UserPatchDto) => { diff --git a/sona/week7/mission1/src/routes.tsx b/sona/week7/mission1/src/routes.tsx index 00ede0e8..22144ac8 100644 --- a/sona/week7/mission1/src/routes.tsx +++ b/sona/week7/mission1/src/routes.tsx @@ -9,6 +9,7 @@ import Layout from "./layout/HomeLayout"; import Home from "./pages/Home"; import CardDetail from "./pages/CardDetail"; import Comment from "./pages/Comment"; +import LpDetailEdit from "./pages/LpDetailEdit"; //로그인 필요없는 페이지 const publicRoutes: RouteObject[] = [ { @@ -28,6 +29,7 @@ const publicRoutes: RouteObject[] = [ path: "signup", element: , }, + { path: "v1/auth/google/callback", element: }, ], }, @@ -48,6 +50,7 @@ const protectedRoutes: RouteObject[] = [ path: "lp/:id/comment", element: , }, + { path: "lp/:id/edit", element: }, ], }, ]; diff --git a/sona/week7/mission1/src/types/common.ts b/sona/week7/mission1/src/types/common.ts index 16a4080f..afd2982c 100644 --- a/sona/week7/mission1/src/types/common.ts +++ b/sona/week7/mission1/src/types/common.ts @@ -48,3 +48,14 @@ export type UserPatchDto = { bio?: string; avatar?: string; }; + +//author +export type Author = { + id: number; + name: string; + email: string; + bio: null; + avatar: null; + createdAt: Date; + updatedAt: Date; +}; diff --git a/sona/week7/mission1/src/types/lpDetail.ts b/sona/week7/mission1/src/types/lpDetail.ts new file mode 100644 index 00000000..1387217d --- /dev/null +++ b/sona/week7/mission1/src/types/lpDetail.ts @@ -0,0 +1,18 @@ +import { Author, CommenResponse } from "./common"; +import { Likes, Tag } from "./lp"; + +export type LpDetail = { + id: number; + title: string; + content: string; + thumbnail: string; + published: boolean; + authorId: number; + createdAt: string; // 또는 Date, 실제 사용하는 포맷에 맞게 + updatedAt: string; + tags: Tag[]; + likes: Likes[]; + author: Author; +}; + +export type LpDetailResponseDto = CommenResponse; From d5a5fc607334c3370cb5fbbd77bbe527fe9b2ef2 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Thu, 15 May 2025 22:30:20 +0900 Subject: [PATCH 16/28] =?UTF-8?q?=E2=9C=A8=20feat:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CardDetail.tsx | 20 +++++++++++++++++++- sona/week7/mission1/src/pages/Home.tsx | 7 +++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index f9b1e201..51aab50f 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -4,6 +4,8 @@ import Comment from "./Comment"; import useGetProfile from "../hooks/useGetProfile"; import useGetLpDetail from "../hooks/useGetLpDetail"; +import { useMutation } from "@tanstack/react-query"; +import axiosInstance from "../apis/axios"; export default function CardDetail() { const { id } = useParams(); @@ -14,6 +16,19 @@ export default function CardDetail() { const { user } = useGetProfile(); console.log(data); + + const deletePost = useMutation({ + mutationFn: (lpId) => { + return axiosInstance.delete(`v1/lps/${lpId}`); + }, + onSuccess: () => { + alert("게시글이 삭제되었습니다"); + }, + onError: (err) => { + console.log(err); + }, + }); + return ( <>
    @@ -32,7 +47,10 @@ export default function CardDetail() {
    {data?.title}
    {/* 삭제 수정 */} {user?.id === data?.author?.id && ( -
    +
    deletePost.mutate(lpId)} + > diff --git a/sona/week7/mission1/src/pages/Home.tsx b/sona/week7/mission1/src/pages/Home.tsx index 52f9b32b..f70cda20 100644 --- a/sona/week7/mission1/src/pages/Home.tsx +++ b/sona/week7/mission1/src/pages/Home.tsx @@ -15,8 +15,11 @@ export default function Home() { PAGENATION_ORDER.desc ); - const { data, isFetching, isPending, isError, hasNextPage, fetchNextPage } = - useGetInfiniteLpList(5, "", sortOrder); + const { data, isFetching, hasNextPage, fetchNextPage } = useGetInfiniteLpList( + 5, + "", + sortOrder + ); const { ref, inView } = useInView({ threshold: 0 }); From 6428231f40d46d53b024bc47f57b6202add0238b Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Thu, 15 May 2025 22:35:12 +0900 Subject: [PATCH 17/28] =?UTF-8?q?=E2=9C=A8=20feat:=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CardDetail.tsx | 23 ++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index 51aab50f..ecf688ee 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -19,7 +19,7 @@ export default function CardDetail() { const deletePost = useMutation({ mutationFn: (lpId) => { - return axiosInstance.delete(`v1/lps/${lpId}`); + return axiosInstance.delete(`/v1/lps/${lpId}`); }, onSuccess: () => { alert("게시글이 삭제되었습니다"); @@ -29,6 +29,18 @@ export default function CardDetail() { }, }); + const likeOn = useMutation({ + mutationFn: (lpId) => { + return axiosInstance.post(`/v1/lps/${lpId}/likes`); + }, + onSuccess: () => { + alert("좋아요가 추가되었습니다"); + }, + onError: (err) => { + console.log(err); + }, + }); + return ( <>
    @@ -85,7 +97,14 @@ export default function CardDetail() {
    - + {/* 좋아요 */} + likeOn.mutate(lpId)} + /> +

    {data?.likes?.length}

    From 17a4d167a3792643bb7554d566589453f3a6fe41 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Thu, 15 May 2025 23:36:48 +0900 Subject: [PATCH 18/28] =?UTF-8?q?=F0=9F=9A=A7=20cont:?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CardDetail.tsx | 32 ++++++++++++------- sona/week7/mission1/src/pages/Comment.tsx | 9 +++++- sona/week7/mission1/src/pages/CommentItem.tsx | 10 ++++-- sona/week7/mission1/src/pages/LpModal.tsx | 7 ++-- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index ecf688ee..26348230 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -4,18 +4,21 @@ import Comment from "./Comment"; import useGetProfile from "../hooks/useGetProfile"; import useGetLpDetail from "../hooks/useGetLpDetail"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import axiosInstance from "../apis/axios"; +import { QUERY_KEY } from "../constants/key"; export default function CardDetail() { + const queryClient = useQueryClient(); + const { id } = useParams(); const lpId = Number(id); const { data } = useGetLpDetail(lpId); - console.log(data); + // console.log(data); const { user } = useGetProfile(); - console.log(data); + // console.log(data); const deletePost = useMutation({ mutationFn: (lpId) => { @@ -34,13 +37,20 @@ export default function CardDetail() { return axiosInstance.post(`/v1/lps/${lpId}/likes`); }, onSuccess: () => { - alert("좋아요가 추가되었습니다"); + // alert("좋아요가 추가되었습니다"); + queryClient.invalidateQueries({ + queryKey: [QUERY_KEY.lpDetail, lpId], + }); }, onError: (err) => { console.log(err); }, }); - + console.log(user?.id); + console.log(data?.authorId); + // if (!data || !user) { + // return
    Loading
    ; + // } return ( <>
    @@ -58,12 +68,12 @@ export default function CardDetail() {
    {data?.title}
    {/* 삭제 수정 */} - {user?.id === data?.author?.id && ( -
    deletePost.mutate(lpId)} - > - diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx index 01c5c72d..90a2d25f 100644 --- a/sona/week7/mission1/src/pages/Comment.tsx +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -8,8 +8,9 @@ import { useEffect, useState } from "react"; import CommentSkeleton from "./CommentSkeleton"; import { PAGENATION_ORDER } from "../enums/common"; import SortComponent from "./SortComponent"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import axiosInstance from "../apis/axios"; +import { QUERY_KEY } from "../constants/key"; type FormFields = { content: string; @@ -19,6 +20,9 @@ export default function Comment() { const [sortOrder, setSortOrder] = useState( PAGENATION_ORDER.desc ); + + const queryClient = useQueryClient(); + const { id } = useParams(); const lpId = Number(id); // const { data: comment, isLoading, isError } = useGetCommentList({ lpId }); @@ -43,6 +47,9 @@ export default function Comment() { onSuccess: () => { alert("댓글작성 성공"); reset(); + queryClient.invalidateQueries({ + queryKey: [QUERY_KEY.comment], + }); }, onError: () => { alert("댓글작성 실패"); diff --git a/sona/week7/mission1/src/pages/CommentItem.tsx b/sona/week7/mission1/src/pages/CommentItem.tsx index d5623ba5..857f4eb6 100644 --- a/sona/week7/mission1/src/pages/CommentItem.tsx +++ b/sona/week7/mission1/src/pages/CommentItem.tsx @@ -1,11 +1,12 @@ import { useState } from "react"; import { Comment } from "../types/comment"; -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import axiosInstance from "../apis/axios"; import { CommentPatchDto, LpDeleteDto } from "../types/common"; import InputField from "../components/InputField"; import { useForm } from "react-hook-form"; import useGetProfile from "../hooks/useGetProfile"; +import { QUERY_KEY } from "../constants/key"; interface CommentProps { comment: Comment; } @@ -14,12 +15,14 @@ interface CommentForm { content: string; } export default function CommentItem({ comment }: CommentProps) { + const queryClient = useQueryClient(); + // console.log(comment); const { user } = useGetProfile(); const [isOpenCModeal, setOpenCModal] = useState(false); - console.log(comment); + // console.log(comment); //삭제 const deleteBtn = useMutation({ mutationFn: ({ commentId, lpId }: LpDeleteDto) => { @@ -42,6 +45,9 @@ export default function CommentItem({ comment }: CommentProps) { }, onSuccess: () => { alert("댓글이 수정되었습니다"); + queryClient.invalidateQueries({ + queryKey: [QUERY_KEY.comment], + }); }, onError: (err) => { console.log(err); diff --git a/sona/week7/mission1/src/pages/LpModal.tsx b/sona/week7/mission1/src/pages/LpModal.tsx index c2c95222..b034a35c 100644 --- a/sona/week7/mission1/src/pages/LpModal.tsx +++ b/sona/week7/mission1/src/pages/LpModal.tsx @@ -1,10 +1,11 @@ -import { useMutation } from "@tanstack/react-query"; +import { useMutation, useQueryClient } from "@tanstack/react-query"; import InputField from "../components/InputField"; import { TagItem } from "./TagItem"; import axiosInstance from "../apis/axios"; import { LpBodyPost } from "../types/lpPost"; import { useForm } from "react-hook-form"; import { useRef, useState } from "react"; +import { QUERY_KEY } from "../constants/key"; interface LpModalProps { onClose: () => void; @@ -47,6 +48,7 @@ export default function LpModal({ } setTagInputValue(""); }; + const queryClient = useQueryClient(); const { register, @@ -95,6 +97,7 @@ export default function LpModal({ alert(isEdit ? "수정 완료" : "추가 완료"); onSubmitSuccess?.(); onClose(); + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.lps] }); }, onError: (err) => console.error(err), }); @@ -138,7 +141,7 @@ export default function LpModal({ LP 이미지
    From c0c831876f85935908fa9dd2f4ce65a1a06bce4b Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 00:17:40 +0900 Subject: [PATCH 19/28] . --- sona/week7/mission1/src/pages/MyPage.tsx | 1 + sona/week7/mission1/src/pages/Navbar.tsx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/sona/week7/mission1/src/pages/MyPage.tsx b/sona/week7/mission1/src/pages/MyPage.tsx index 2368ceba..afedebee 100644 --- a/sona/week7/mission1/src/pages/MyPage.tsx +++ b/sona/week7/mission1/src/pages/MyPage.tsx @@ -47,6 +47,7 @@ const MyPage = () => { }, onSuccess: () => { alert("성공하였습니다"); + navigate("/"); }, onError: (err) => { console.log(err); diff --git a/sona/week7/mission1/src/pages/Navbar.tsx b/sona/week7/mission1/src/pages/Navbar.tsx index 52c15c74..310ddb46 100644 --- a/sona/week7/mission1/src/pages/Navbar.tsx +++ b/sona/week7/mission1/src/pages/Navbar.tsx @@ -1,4 +1,4 @@ -import { use, useState } from "react"; +import { useState } from "react"; import { Link, useNavigate } from "react-router-dom"; import { useAuth } from "../context/AuthContext"; import useGetProfile from "../hooks/useGetProfile"; From 88e377334ef2de10c7e813767c587009bbf1fef6 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 15:14:19 +0900 Subject: [PATCH 20/28] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=20lp=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=20=EB=B2=84=EA=B7=B8=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CardDetail.tsx | 14 ++++++++------ sona/week7/mission1/src/pages/LpCard.tsx | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index 26348230..1516c157 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -1,4 +1,4 @@ -import { Link, useParams } from "react-router-dom"; +import { Link, useNavigate, useParams } from "react-router-dom"; import getTimePassed from "../utils/dateCalculate"; import Comment from "./Comment"; @@ -10,22 +10,23 @@ import { QUERY_KEY } from "../constants/key"; export default function CardDetail() { const queryClient = useQueryClient(); - + const naviagte = useNavigate(); const { id } = useParams(); const lpId = Number(id); const { data } = useGetLpDetail(lpId); - // console.log(data); const { user } = useGetProfile(); + console.log(user); // console.log(data); const deletePost = useMutation({ - mutationFn: (lpId) => { + mutationFn: (lpId: number) => { return axiosInstance.delete(`/v1/lps/${lpId}`); }, onSuccess: () => { alert("게시글이 삭제되었습니다"); + naviagte(-1); }, onError: (err) => { console.log(err); @@ -46,11 +47,12 @@ export default function CardDetail() { console.log(err); }, }); - console.log(user?.id); - console.log(data?.authorId); + console.log("userid", user?.id); + console.log("글쓴이id", data?.authorId); // if (!data || !user) { // return
    Loading
    ; // } + return ( <>
    diff --git a/sona/week7/mission1/src/pages/LpCard.tsx b/sona/week7/mission1/src/pages/LpCard.tsx index cfbb1b32..172fc659 100644 --- a/sona/week7/mission1/src/pages/LpCard.tsx +++ b/sona/week7/mission1/src/pages/LpCard.tsx @@ -8,7 +8,7 @@ interface LpProps { export default function LpCard({ item }: LpProps) { // console.log(item.thumbnail); - // console.log(item); + // console.log(item); return ( <>
    From 29fd7926b84d3a010693978c3880cf34bdc8bc0e Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 15:29:15 +0900 Subject: [PATCH 21/28] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A2=8B=EC=95=84?= =?UTF-8?q?=EC=9A=94=ED=86=A0=EA=B8=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/CardDetail.tsx | 22 +++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index 1516c157..fe1358cd 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -16,7 +16,7 @@ export default function CardDetail() { const { data } = useGetLpDetail(lpId); const { user } = useGetProfile(); - console.log(user); + // console.log(user); // console.log(data); @@ -33,12 +33,17 @@ export default function CardDetail() { }, }); - const likeOn = useMutation({ + const likeToggleBtn = useMutation({ mutationFn: (lpId) => { - return axiosInstance.post(`/v1/lps/${lpId}/likes`); + const hasLike = data?.likes?.some((item) => item.userId === user?.id); + + if (hasLike) { + return axiosInstance.delete(`/v1/lps/${lpId}/likes`); + } else { + return axiosInstance.post(`/v1/lps/${lpId}/likes`); + } }, onSuccess: () => { - // alert("좋아요가 추가되었습니다"); queryClient.invalidateQueries({ queryKey: [QUERY_KEY.lpDetail, lpId], }); @@ -47,11 +52,8 @@ export default function CardDetail() { console.log(err); }, }); - console.log("userid", user?.id); - console.log("글쓴이id", data?.authorId); - // if (!data || !user) { - // return
    Loading
    ; - // } + // console.log("userid", user?.id); + // console.log("글쓴이id", data?.authorId); return ( <> @@ -114,7 +116,7 @@ export default function CardDetail() { className="size-7" src="/detailHart.svg" alt="" - onClick={() => likeOn.mutate(lpId)} + onClick={() => likeToggleBtn.mutate(lpId)} />

    {data?.likes?.length}

    From 5e771198e8c813700ed702cd07c23d270408de48 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 15:45:34 +0900 Subject: [PATCH 22/28] =?UTF-8?q?=E2=9C=A8=20feat:=20=EC=A1=B0=ED=95=AD?= =?UTF-8?q?=EC=9A=94=20ui?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/public/emptyHart.svg | 1 + sona/week7/mission1/src/pages/CardDetail.tsx | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 sona/week7/mission1/public/emptyHart.svg diff --git a/sona/week7/mission1/public/emptyHart.svg b/sona/week7/mission1/public/emptyHart.svg new file mode 100644 index 00000000..0207a48e --- /dev/null +++ b/sona/week7/mission1/public/emptyHart.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/sona/week7/mission1/src/pages/CardDetail.tsx b/sona/week7/mission1/src/pages/CardDetail.tsx index fe1358cd..421dde73 100644 --- a/sona/week7/mission1/src/pages/CardDetail.tsx +++ b/sona/week7/mission1/src/pages/CardDetail.tsx @@ -32,11 +32,10 @@ export default function CardDetail() { console.log(err); }, }); + const hasLike = data?.likes?.some((item) => item.userId === user?.id); const likeToggleBtn = useMutation({ mutationFn: (lpId) => { - const hasLike = data?.likes?.some((item) => item.userId === user?.id); - if (hasLike) { return axiosInstance.delete(`/v1/lps/${lpId}/likes`); } else { @@ -114,7 +113,7 @@ export default function CardDetail() { {/* 좋아요 */} likeToggleBtn.mutate(lpId)} /> From 2a455ed837f02ae8ca92d88420511413d323f4d1 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 16:45:55 +0900 Subject: [PATCH 23/28] =?UTF-8?q?=E2=9A=B0=EF=B8=8F=20cleanup:=20hook?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=A0=95=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/LpDetailEdit.tsx | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/sona/week7/mission1/src/pages/LpDetailEdit.tsx b/sona/week7/mission1/src/pages/LpDetailEdit.tsx index 51736140..c5b78f70 100644 --- a/sona/week7/mission1/src/pages/LpDetailEdit.tsx +++ b/sona/week7/mission1/src/pages/LpDetailEdit.tsx @@ -5,6 +5,7 @@ import axiosInstance from "../apis/axios"; import { QUERY_KEY } from "../constants/key"; import LpModal from "./LpModal"; import { Tag } from "../types/lp"; +import useGetLpDetail from "../hooks/useGetLpDetail"; export default function LpDetailEdit() { const { id } = useParams(); @@ -12,12 +13,7 @@ export default function LpDetailEdit() { const navigate = useNavigate(); const [isModalOpen, setIsModalOpen] = useState(true); - const { data, isLoading, isError } = useQuery({ - queryKey: [QUERY_KEY.lpDetail, lpId], - queryFn: () => axiosInstance.get(`/v1/lps/${lpId}`), - select: (res) => res.data.data, - enabled: !!lpId, - }); + const { data, isLoading, isError } = useGetLpDetail(lpId); if (isLoading) return

    로딩 중...

    ; if (isError || !data) From 8089dfd882955ed53bdce9f63d28c81d3027db05 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 16:55:02 +0900 Subject: [PATCH 24/28] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=EB=AA=A8=EB=8B=AC?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/LpDetailEdit.tsx | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/sona/week7/mission1/src/pages/LpDetailEdit.tsx b/sona/week7/mission1/src/pages/LpDetailEdit.tsx index c5b78f70..2a3b47cf 100644 --- a/sona/week7/mission1/src/pages/LpDetailEdit.tsx +++ b/sona/week7/mission1/src/pages/LpDetailEdit.tsx @@ -1,8 +1,6 @@ import { useState } from "react"; import { useParams, useNavigate } from "react-router-dom"; -import { useQuery } from "@tanstack/react-query"; -import axiosInstance from "../apis/axios"; -import { QUERY_KEY } from "../constants/key"; + import LpModal from "./LpModal"; import { Tag } from "../types/lp"; import useGetLpDetail from "../hooks/useGetLpDetail"; @@ -30,9 +28,9 @@ export default function LpDetailEdit() { thumbnail: data.thumbnail, tags: data.tags.map((t: Tag) => t.name), // tags가 [{ id, name }] 형태라면 }} - onClose={() => navigate(-1)} + onClose={() => setIsModalOpen(false)} onSubmitSuccess={() => { - navigate(`/lps/${lpId}`); + navigate(-1); }} /> )} From b493dce392ecff847a62317dc1a222375a20929b Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 17:04:17 +0900 Subject: [PATCH 25/28] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20refactor:=20cash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/Comment.tsx | 1 - sona/week7/mission1/src/pages/CommentItem.tsx | 13 ++++++++++--- sona/week7/mission1/src/pages/Home.tsx | 6 +++--- sona/week7/mission1/src/pages/LpModal.tsx | 12 +++++++----- 4 files changed, 20 insertions(+), 12 deletions(-) diff --git a/sona/week7/mission1/src/pages/Comment.tsx b/sona/week7/mission1/src/pages/Comment.tsx index 90a2d25f..74e3b568 100644 --- a/sona/week7/mission1/src/pages/Comment.tsx +++ b/sona/week7/mission1/src/pages/Comment.tsx @@ -45,7 +45,6 @@ export default function Comment() { mutationFn: (comment) => axiosInstance.post(`/v1/lps/${lpId}/comments`, comment), onSuccess: () => { - alert("댓글작성 성공"); reset(); queryClient.invalidateQueries({ queryKey: [QUERY_KEY.comment], diff --git a/sona/week7/mission1/src/pages/CommentItem.tsx b/sona/week7/mission1/src/pages/CommentItem.tsx index 857f4eb6..94a68da4 100644 --- a/sona/week7/mission1/src/pages/CommentItem.tsx +++ b/sona/week7/mission1/src/pages/CommentItem.tsx @@ -29,7 +29,9 @@ export default function CommentItem({ comment }: CommentProps) { return axiosInstance.delete(`/v1/lps/${lpId}/comments/${commentId}`); }, onSuccess: () => { - alert("댓글이 삭제되었습니다"); + queryClient.invalidateQueries({ + queryKey: [QUERY_KEY.comment], + }); }, onError: (err) => { console.log(err); @@ -44,7 +46,8 @@ export default function CommentItem({ comment }: CommentProps) { }); }, onSuccess: () => { - alert("댓글이 수정되었습니다"); + setEdit(false); + reset(); queryClient.invalidateQueries({ queryKey: [QUERY_KEY.comment], }); @@ -60,6 +63,7 @@ export default function CommentItem({ comment }: CommentProps) { register, handleSubmit, formState: { errors }, + reset, } = useForm(); const onEditSubmit = (data: { content: string }) => { @@ -114,7 +118,10 @@ export default function CommentItem({ comment }: CommentProps) {
    @@ -63,9 +63,9 @@ export default function Home() { {/* 등록 모달 */} {isModalOpen && ( setIsModalOpen(false)} + onClose={() => setIsModalOpen(false)} //모달 직접 닫기 onSubmitSuccess={() => { - setIsModalOpen(false); + setIsModalOpen(false); //성공후 모달 닫음 }} /> )} diff --git a/sona/week7/mission1/src/pages/LpModal.tsx b/sona/week7/mission1/src/pages/LpModal.tsx index b034a35c..43268f5e 100644 --- a/sona/week7/mission1/src/pages/LpModal.tsx +++ b/sona/week7/mission1/src/pages/LpModal.tsx @@ -33,7 +33,7 @@ export default function LpModal({ lpId, initialData, }: LpModalProps) { - const isEdit = !!lpId; + const isEdit = !!lpId; //수정 mode const [tagInputValue, setTagInputValue] = useState(""); const [tags, setTags] = useState(initialData?.tags ?? []); const [preview, setPreview] = useState( @@ -43,6 +43,7 @@ export default function LpModal({ const AddTag = () => { const tagNode = tagInputValue.trim(); + if (tagNode && !tags.includes(tagNode)) { setTags([...tags, tagNode]); } @@ -63,7 +64,7 @@ export default function LpModal({ }); const mutationFn = async () => { - const formValues = getValues(); + const formValues = getValues(); //formData const postBody: LpBodyPost = { title: formValues.lpName, content: formValues.lpContent, @@ -91,6 +92,7 @@ export default function LpModal({ : axiosInstance.post(`/v1/lps`, postBody); }; + //Lp const addOrUpdateLp = useMutation({ mutationFn, onSuccess: () => { @@ -105,8 +107,8 @@ export default function LpModal({ const imageChange = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (file) { - setPreview(URL.createObjectURL(file)); - fileInput.current = file; + setPreview(URL.createObjectURL(file)); //미리보기 + fileInput.current = file; //업로드파일 저장 } }; @@ -135,7 +137,7 @@ export default function LpModal({ accept="image/*" id="image-upload" {...register("file")} - onChange={imageChange} + onChange={imageChange} //이미지 바뀌면 미리보기 className="hidden" /> Date: Fri, 16 May 2025 17:08:09 +0900 Subject: [PATCH 26/28] =?UTF-8?q?=F0=9F=92=84=20style:myPage=20ui=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/MyPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sona/week7/mission1/src/pages/MyPage.tsx b/sona/week7/mission1/src/pages/MyPage.tsx index afedebee..894bf54e 100644 --- a/sona/week7/mission1/src/pages/MyPage.tsx +++ b/sona/week7/mission1/src/pages/MyPage.tsx @@ -70,7 +70,7 @@ const MyPage = () => {
    { )}
    +
    ); } From 0f1bc9599a304df73b25cd0cd19a578c297a3515 Mon Sep 17 00:00:00 2001 From: JeonSuna Date: Fri, 16 May 2025 19:10:07 +0900 Subject: [PATCH 28/28] =?UTF-8?q?=F0=9F=90=9E=20fix:=20=EA=B2=8C=EC=8B=9C?= =?UTF-8?q?=EA=B8=80=20=EC=88=98=EC=A0=95=20=EC=82=AD=EC=A0=9C=20=EB=B2=84?= =?UTF-8?q?=EA=B7=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sona/week7/mission1/src/pages/LpModal.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/sona/week7/mission1/src/pages/LpModal.tsx b/sona/week7/mission1/src/pages/LpModal.tsx index 43268f5e..69f64833 100644 --- a/sona/week7/mission1/src/pages/LpModal.tsx +++ b/sona/week7/mission1/src/pages/LpModal.tsx @@ -99,6 +99,7 @@ export default function LpModal({ alert(isEdit ? "수정 완료" : "추가 완료"); onSubmitSuccess?.(); onClose(); + queryClient.invalidateQueries({ queryKey: [QUERY_KEY.lpDetail, lpId] }); queryClient.invalidateQueries({ queryKey: [QUERY_KEY.lps] }); }, onError: (err) => console.error(err),